diff --git a/Modules/Classification/CLCore/CMakeLists.txt b/Modules/Classification/CLCore/CMakeLists.txt index 2af0a4d28e..cd84cde61e 100644 --- a/Modules/Classification/CLCore/CMakeLists.txt +++ b/Modules/Classification/CLCore/CMakeLists.txt @@ -1,7 +1,7 @@ MITK_CREATE_MODULE( - DEPENDS MitkCore + DEPENDS MitkCore MitkCommandLine PACKAGE_DEPENDS PUBLIC Eigen ) #add_subdirectory(test) diff --git a/Modules/Classification/CLCore/files.cmake b/Modules/Classification/CLCore/files.cmake index 0e43386e0d..c3ae208ff9 100644 --- a/Modules/Classification/CLCore/files.cmake +++ b/Modules/Classification/CLCore/files.cmake @@ -1,10 +1,11 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkConfigurationHolder.cpp mitkAbstractClassifier.cpp mitkAbstractGlobalImageFeature.cpp + mitkIntensityQuantifier.cpp ) set( TOOL_FILES ) diff --git a/Modules/Classification/CLCore/include/mitkAbstractGlobalImageFeature.h b/Modules/Classification/CLCore/include/mitkAbstractGlobalImageFeature.h index 12bb3ba662..4cb359e334 100644 --- a/Modules/Classification/CLCore/include/mitkAbstractGlobalImageFeature.h +++ b/Modules/Classification/CLCore/include/mitkAbstractGlobalImageFeature.h @@ -1,78 +1,284 @@ /*=================================================================== 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 mitkAbstractGlobalImageFeature_h #define mitkAbstractGlobalImageFeature_h #include #include #include -// Eigen -#include +#include + +#include // STD Includes +// Eigen +#include + // MITK includes #include namespace mitk { + /** + * + * + * ## Histogram Configuration ## + * Most Feature Generation Classes that use histograms use the same parameters and + * initialization logic. In general, all information can be passed either by the corresponding + * Setter (which does not differenciate between global setting and feature specific setting) and + * a parameter object which can be obtained from the command line arguments, for example. + * + * If the image values are used for the initializiation of the histogram, it can be defined + * whether the whole image is used or only the masked areas to find minima and maxima. This is + * done by the option SetIgnoreMask or the corrsponding options + * -NAME::ignore-mask-for-histogram and -ignore-mask-for-histogram. If these are + * true, the whole image is used for the calculation. + * + * Depending on the passed arguments, different initialization methods are used. The initialization + * is in the following order: + * - If Minimum Intensity, Maximum Intensity, and Binsize: The histogram is + * initialized between the minimum and maximum intensity. the number of bins is determined by the + * binsize. If the distance between minimum and maximum is not a multiple of the binsize, the maximum + * is increase so that it is. + * - Minimum Intensity, Bins, and Binsize: The histogram is initialized with the + * given binsize, and the intensity range from the minimum to \f$maximum = minimum + binsize*bins\f$. + * - Minimum Intensity, Maximum Intensity, and Bins: The histogram is initialized + * between the given minimum and maximum intensity. The binsize is calculated so that the number + * of bins is equal to the given number of bins. + * - Binsize, and Minimum Intensity: The maximum is set to the maximum that + * occur in the given image. Depending if the mask is considered or not, either only masked voxels or + * the whole image is used for the calculation. The initialization is then equal as if the minimum + * and maximum would have been given right from the beginning. + * - Binsize, and Maximum Intensity: The minimum intensity is set to the minimum that + * occur in the given image. Depending if the mask is considered or not, either only masked voxels or + * the whole image is used for the calculation. The initialization is then equal as if the minimum + * and maximum would have been given right from the beginning. + * - Binsize: The maximum and the minimum intensity is set to the minimum and maximum that + * occur in the given image. Depending if the mask is considered or not, either only masked voxels or + * the whole image is used for the calculation. The initialization is then equal as if the minimum + * and maximum would have been given right from the beginning. + * - Bins, and Minimum Intensity: The maximum is calculated from the image. Depending + * if the mask is considered or not, either only masked voxels or the whole image is used for the calculation. The histogram is + * then initialized as if these values would have been given as minimum and maximum intensity. + * - Bins, and Maximum Intensity: The minimum is calculated from the image. Depending + * if the mask is considered or not, either only masked voxels or the whole image is used for the calculation. The histogram is + * then initialized as if these values would have been given as minimum and maximum intensity. + * - Bins: The minimum and the maximum is calculated from the image. Depending + * if the mask is considered or not, either only masked voxels or * the whole image is used for the calculation. The histogram is + * then initialized as if these values would have been given as minimum and maximum intensity. + * - No Parameter given:The minimum and maximum intensity from the whole image or masked image is calculated and + * the histogram then initialized to this with a standard number of bins (Is set by each filter on its own.) + * + * ### Remark about command line parameter#### + * There are generally two options to set a parameter via the command line. A global one that works for + * all filters that use histograms and a local one that set this parameter specific for this filter. The + * local parameters start with the filter name (Indiciated by NAME) followed by two colons, for example + * vol::min to set the minimum intensity for the volume filter. The global parameter is overwritten + * by the local parameter, if it is specified. Otherwise, it is still valid. If this prevents the specification + * of an histogram initialization method (for example, because the binsize is globally specified but the histogram + * should be initialized using a fixed numbe of bins), the parameter NAME::ignore-global-histogram can be passed. + * Then, all global histogram parameters are ignored and only local ones are used. + * + * The maximum intensity can be set by different command line parameters: global for all filters that use histograms + * by -minimum-intensity and -minimum. Alternative it can be set only for this filter by + * -NAME::minimum and -NAME::min. + * + * The minimum intensity can be set by different command line parameters: global for all filters that use histograms + * by -maximum-intensity and -maximum. Alternative it can be set only for this filter by + * \NAME::maximum and NAME::max. + * + * The binsize can be set by different command line parameters: global for all filters that use histograms + * by -binsize. Alternative it can be set only for this filter by + * \NAME::binsize. + * + * The number of bins can be set by different command line parameters: global for all filters that use histograms + * by -bins. Alternative it can be set only for this filter by + * \NAME::bins. + + + * ### Note to the developers ### + * All features are supposed to work the same way if a histogram is used somewhere in + * the code. For this, each derived class that makes use of a histogram should use + * the Quantifier object. In order to use this object correctly, the AddArguments-Function should + * contain the line AddQuantifierArguments(parser);, the CalculateFeaturesUsingParameters function + * should contain the line InitializeQuantifierFromParameters(feature, mask); and the CalculateFeatures function + * sould contain the line InitializeQuantifier(image, mask);. These function + * calls ensure that the necessary options are given to the configuration file, and that the initialization + * of the quantifier is done correctly. This ensures an consistend behavior over all FeatureGeneration Classes. + * + */ class MITKCLCORE_EXPORT AbstractGlobalImageFeature : public BaseData { public: + mitkClassMacro(AbstractGlobalImageFeature, BaseData) typedef std::vector< std::pair > FeatureListType; - typedef std::vector< std::string> FeatureNameListType; + typedef std::vector< std::string> FeatureNameListType; + typedef std::map ParameterTypes; + + /** + * \brief Calculates the feature of this abstact interface. Does not necessarily considers the parameter settings. + */ + virtual FeatureListType CalculateFeatures(const Image::Pointer & feature, const Image::Pointer &mask) = 0; /** - * \brief Calculates the feature of this abstact interface. + * \brief Calculates the feature of this abstact interface. Does not necessarily considers the parameter settings. */ - virtual FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature) = 0; + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList) = 0; /** * \brief Returns a list of the names of all features that are calculated from this class */ virtual FeatureNameListType GetFeatureNames() = 0; + /** + * \brief Adds an additional Separator to the name of the feature, which encodes the used parameters + */ + virtual std::string GetCurrentFeatureEncoding(); + + /** + * \brief Returns a string that encodes the feature class name. + * + * This Feature returns a string that should be put before every other feature + * value. It has the format [::]::[::]. + * The options and are only added, if the corresponding + * options are set. + */ + std::string FeatureDescriptionPrefix(); + + itkSetMacro(Prefix, std::string); + itkSetMacro(ShortName, std::string); + itkSetMacro(LongName, std::string); + itkSetMacro(FeatureClassName, std::string); + itkSetMacro(Direction, int); + void SetParameter(ParameterTypes param) { m_Parameter=param; }; + + itkGetConstMacro(Prefix, std::string); + itkGetConstMacro(ShortName, std::string); + itkGetConstMacro(LongName, std::string); + itkGetConstMacro(FeatureClassName, std::string); + itkGetConstMacro(Parameter, ParameterTypes); + + itkSetMacro(UseQuantifier, bool); + itkGetConstMacro(UseQuantifier, bool); + itkSetMacro(Quantifier, IntensityQuantifier::Pointer); + itkGetMacro(Quantifier, IntensityQuantifier::Pointer); + + itkGetConstMacro(Direction, int); + + itkSetMacro(MinimumIntensity, double); + itkSetMacro(UseMinimumIntensity, bool); + itkSetMacro(MaximumIntensity, double); + itkSetMacro(UseMaximumIntensity, bool); + itkGetConstMacro(MinimumIntensity, double); + itkGetConstMacro(UseMinimumIntensity, bool); + itkGetConstMacro(MaximumIntensity, double); + itkGetConstMacro(UseMaximumIntensity, bool); + + + itkSetMacro(Binsize, double); + itkSetMacro(UseBinsize, bool); + itkGetConstMacro(Binsize, double); + itkGetConstMacro(UseBinsize, bool); + + itkSetMacro(MorphMask, mitk::Image::Pointer); + itkGetConstMacro(MorphMask, mitk::Image::Pointer); + + itkSetMacro(Bins, int); + itkSetMacro(UseBins, bool); + itkGetConstMacro(UseBins, bool); + itkGetConstMacro(Bins, int); + + itkSetMacro(IgnoreMask, bool); + itkGetConstMacro(IgnoreMask, bool); + + itkSetMacro(EncodeParameters, bool); + itkGetConstMacro(EncodeParameters, bool); + + std::string GetOptionPrefix() const + { + if (m_Prefix.length() > 0) + return m_Prefix + "::" + m_ShortName; + return m_ShortName; + + } + + virtual void AddArguments(mitkCommandLineParser &parser) = 0; + std::vector SplitDouble(std::string str, char delimiter); + + void AddQuantifierArguments(mitkCommandLineParser &parser); + void InitializeQuantifierFromParameters(const Image::Pointer & feature, const Image::Pointer &mask,unsigned int defaultBins = 256); + void InitializeQuantifier(const Image::Pointer & feature, const Image::Pointer &mask, unsigned int defaultBins = 256); + std::string QuantifierParameterString(); + public: //#ifndef DOXYGEN_SKIP void SetRequestedRegionToLargestPossibleRegion() override {}; bool RequestedRegionIsOutsideOfTheBufferedRegion() override { return true; }; bool VerifyRequestedRegion() override { return false; }; void SetRequestedRegion (const itk::DataObject * /*data*/) override {}; // Override bool IsEmpty() const override { if(IsInitialized() == false) return true; const TimeGeometry* timeGeometry = const_cast(this)->GetUpdatedTimeGeometry(); if(timeGeometry == nullptr) return true; return false; } + +private: + std::string m_Prefix; // Prefix before all input parameters + std::string m_ShortName; // Name of all variables + std::string m_LongName; // Long version of the name (For turning on) + std::string m_FeatureClassName; + ParameterTypes m_Parameter; // Parameter setting + + bool m_UseQuantifier = false; + IntensityQuantifier::Pointer m_Quantifier; + + double m_MinimumIntensity = 0; + bool m_UseMinimumIntensity = false; + double m_MaximumIntensity = 100; + bool m_UseMaximumIntensity = false; + bool m_EncodeParameters = false; + + double m_Binsize = 1; + bool m_UseBinsize = false; + + int m_Bins = 256; + bool m_UseBins = true; + int m_Direction = 0; + + bool m_IgnoreMask = false; + + mitk::Image::Pointer m_MorphMask = nullptr; //#endif // Skip Doxygen }; } #endif //mitkAbstractGlobalImageFeature_h diff --git a/Modules/Classification/CLCore/include/mitkConfigFileReader.h b/Modules/Classification/CLCore/include/mitkConfigFileReader.h index 20e9102ff1..eed237f4e0 100644 --- a/Modules/Classification/CLCore/include/mitkConfigFileReader.h +++ b/Modules/Classification/CLCore/include/mitkConfigFileReader.h @@ -1,206 +1,214 @@ /*=================================================================== 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 ConfigFileReader_h #define ConfigFileReader_h #include #include #include #include #include +#include class ConfigFileReader { protected: typedef std::map ContentType; typedef std::map > ListContentType; ContentType m_ConfigContent; ListContentType m_ListContent; std::map m_ListSize; std::string Trim(std::string const& source, char const * delim = " \t\r\n") { std::string result(source); std::string::size_type index = result.find_last_not_of(delim); if(index != std::string::npos) result.erase(++index); index = result.find_first_not_of(delim); if(index != std::string::npos) result.erase(0, index); else result.erase(); return result; } std::string RemoveComment(std::string const& source, char const * delim = "#;") { std::string result(source); std::string::size_type index = result.find_first_of(delim); if(index != std::string::npos) result.erase(++index); return Trim(result); } std::string ListIndex(std::string const& section, unsigned int index) const { std::stringstream stream; stream << section << "/" << index; std::string result = stream.str(); std::transform(result.begin(), result.end(), result.begin(), ::tolower); return result; } std::string ContentIndex(std::string const& section, std::string const& entry) const { std::string result = section + '/' + entry; std::transform(result.begin(), result.end(), result.begin(), ::tolower); return result; } std::string ListSizeIndex(std::string const& section) const { std::string result = section; std::transform(result.begin(), result.end(), result.begin(), ::tolower); return result; } public: ConfigFileReader(std::string const& configFile) { ReadFile (configFile); } void ReadFile(std::string const& filePath) { std::ifstream file(filePath.c_str()); ReadStream(file); file.close(); } void ReadStream (std::istream& stream) { std::string line; std::string name; std::string value; std::string inSection; bool inConfigSection = true; std::string::size_type posEqual; while (std::getline(stream,line)) { line = RemoveComment(line, "#"); if (! line.length()) continue; if (line[0] == '[') { inConfigSection = true; inSection = Trim(line.substr(1,line.find(']')-1)); continue; } if(line[0] == '{') { std::string address = Trim(line.substr(1,line.find('}')-1)); inSection = ListIndex(address, ListSize(address,0)); m_ListSize[ListSizeIndex(address)]++; inConfigSection = false; continue; } if (inConfigSection) { posEqual=line.find('='); name = Trim(line.substr(0,posEqual)); value = Trim(line.substr(posEqual+1)); m_ConfigContent[ContentIndex(inSection, name)]=value; } else { m_ListContent[inSection].push_back(Trim(line)); } } } std::string Value(std::string const& section, std::string const& entry) const { std::string index = ContentIndex(section,entry); if (m_ConfigContent.find(index) == m_ConfigContent.end()) - throw "Entry doesn't exist " + section + entry; + throw std::string("Entry doesn't exist " + section +"::"+ entry); + std::cout << section << "::" << entry << m_ConfigContent.find(index)->second << std::endl; return m_ConfigContent.find(index)->second; } std::string Value(const std::string & section, const std::string & entry, const std::string& standard) { try { return Value(section, entry); } - catch (const char *) { + catch (const std::string) { m_ConfigContent[ContentIndex(section, entry)] = standard; + std::cout << section << "::" << entry << standard << " (default)" << std::endl; return standard; } } int IntValue(const std::string & section, const std::string & entry) const { int result; std::stringstream stream (Value(section, entry)); stream >> result; return result; } int IntValue(const std::string & section, const std::string & entry, int standard) { try { return IntValue(section, entry); } - catch (const char *) { + catch (const std::string) { std::stringstream stream; stream << standard; m_ConfigContent[ContentIndex(section, entry)] = stream.str(); + std::cout << section << "::" << entry << stream.str() << "(default)" << std::endl; return standard; } } std::vector Vector(std::string const& section, unsigned int index) const { if (m_ListContent.find(ListIndex(section, index)) == m_ListContent.end()) - throw "Entry doesn't exist " + section; + { + throw std::string("Entry doesn't exist " + section); + } return m_ListContent.find(ListIndex(section,index))->second; } unsigned int ListSize(std::string const& section) const { if (m_ListSize.find(ListSizeIndex(section)) == m_ListSize.end()) - throw "Entry doesn't exist " + section; + { + throw std::string("Entry doesn't exist " + section); + } return m_ListSize.find(ListSizeIndex(section))->second; } unsigned int ListSize(std::string const& section, unsigned int standard) { try { return ListSize(ListSizeIndex(section)); } catch (...) { m_ListSize[ListSizeIndex(section)] = standard; return standard; } } }; #endif \ No newline at end of file diff --git a/Modules/Classification/CLCore/include/mitkIntensityQuantifier.h b/Modules/Classification/CLCore/include/mitkIntensityQuantifier.h new file mode 100644 index 0000000000..306848c10b --- /dev/null +++ b/Modules/Classification/CLCore/include/mitkIntensityQuantifier.h @@ -0,0 +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. + +===================================================================*/ + + +#ifndef mitkIntensityQuantifier_h +#define mitkIntensityQuantifier_h + +#include + +#include +#include + +namespace mitk +{ +class MITKCLCORE_EXPORT IntensityQuantifier : public BaseData +{ +public: + mitkClassMacro(IntensityQuantifier, BaseData) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + IntensityQuantifier(); + + void InitializeByMinimumMaximum(double minimum, double maximum, unsigned int bins); + void InitializeByBinsizeAndBins(double minimum, unsigned int bins, double binsize); + void InitializeByBinsizeAndMaximum(double minimum, double maximum, double binsize); + void InitializeByImage(mitk::Image::Pointer image, unsigned int bins); + void InitializeByImageAndMaximum(mitk::Image::Pointer image, double maximum, unsigned int bins); + void InitializeByImageAndMinimum(mitk::Image::Pointer image, double minimum, unsigned int bins); + void InitializeByImageRegion(mitk::Image::Pointer image, mitk::Image::Pointer mask, unsigned int bins); + void InitializeByImageRegionAndMinimum(mitk::Image::Pointer image, mitk::Image::Pointer mask, double minimum, unsigned int bins); + void InitializeByImageRegionAndMaximum(mitk::Image::Pointer image, mitk::Image::Pointer mask, double maximum, unsigned int bins); + void InitializeByImageAndBinsize(mitk::Image::Pointer image, double binsize); + void InitializeByImageAndBinsizeAndMinimum(mitk::Image::Pointer image, double minimum, double binsize); + void InitializeByImageAndBinsizeAndMaximum(mitk::Image::Pointer image, double maximum, double binsize); + void InitializeByImageRegionAndBinsize(mitk::Image::Pointer image, mitk::Image::Pointer mask, double binsize); + void InitializeByImageRegionAndBinsizeAndMinimum(mitk::Image::Pointer image, mitk::Image::Pointer mask, double minimum, double binsize); + void InitializeByImageRegionAndBinsizeAndMaximum(mitk::Image::Pointer image, mitk::Image::Pointer mask, double maximum, double binsize); + + unsigned int IntensityToIndex(double intensity); + double IndexToMinimumIntensity(unsigned int index); + double IndexToMeanIntensity(unsigned int index); + double IndexToMaximumIntensity(unsigned int index); + + itkGetConstMacro(Initialized, bool); + itkGetConstMacro(Bins, unsigned int); + itkGetConstMacro(Binsize, double); + itkGetConstMacro(Minimum, double); + itkGetConstMacro(Maximum, double); + +public: + +//#ifndef DOXYGEN_SKIP + + virtual void SetRequestedRegionToLargestPossibleRegion() override {}; + virtual bool RequestedRegionIsOutsideOfTheBufferedRegion() override { return true; }; + virtual bool VerifyRequestedRegion() override { return false; }; + virtual void SetRequestedRegion (const itk::DataObject * /*data*/) override {}; + + // Override + virtual bool IsEmpty() const override + { + if(IsInitialized() == false) + return true; + const TimeGeometry* timeGeometry = const_cast(this)->GetUpdatedTimeGeometry(); + if(timeGeometry == nullptr) + return true; + return false; + } + + +private: + bool m_Initialized; + unsigned int m_Bins; + double m_Binsize; + double m_Minimum; + double m_Maximum; + +}; +} + +#endif //mitkIntensityQuantifier_h diff --git a/Modules/Classification/CLCore/src/mitkAbstractGlobalImageFeature.cpp b/Modules/Classification/CLCore/src/mitkAbstractGlobalImageFeature.cpp index cf41725808..abf55d2692 100644 --- a/Modules/Classification/CLCore/src/mitkAbstractGlobalImageFeature.cpp +++ b/Modules/Classification/CLCore/src/mitkAbstractGlobalImageFeature.cpp @@ -1,17 +1,234 @@ /*=================================================================== 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 + +std::vector mitk::AbstractGlobalImageFeature::SplitDouble(std::string str, char delimiter) { + std::vector internal; + std::stringstream ss(str); // Turn the string into a stream. + std::string tok; + double val; + while (std::getline(ss, tok, delimiter)) { + std::stringstream s2(tok); + s2 >> val; + internal.push_back(val); + } + + return internal; +} + +void mitk::AbstractGlobalImageFeature::AddQuantifierArguments(mitkCommandLineParser &parser) +{ + std::string name = GetOptionPrefix(); + + parser.addArgument(name + "::minimum", name + "::min", mitkCommandLineParser::Float, "Minium Intensity for Quantification", "Defines the minimum Intensity used for Quantification", us::Any()); + parser.addArgument(name + "::maximum", name + "::max", mitkCommandLineParser::Float, "Maximum Intensity for Quantification", "Defines the maximum Intensity used for Quantification", us::Any()); + parser.addArgument(name + "::bins", name + "::bins", mitkCommandLineParser::Int, "Number of Bins", "Define the number of bins that is used ", us::Any()); + parser.addArgument(name + "::binsize", name + "::binsize", mitkCommandLineParser::Float, "Binsize", "Define the size of the used bins", us::Any()); + parser.addArgument(name + "::ignore-global-histogram", name + "::ignore-global-histogram", mitkCommandLineParser::Bool, "Ignore the global histogram Parameters", "Ignores the global histogram parameters", us::Any()); + parser.addArgument(name + "::ignore-mask-for-histogram", name + "::ignore-mask", mitkCommandLineParser::Bool, "Ignore the global histogram Parameters", "Ignores the global histogram parameters", us::Any()); + +} + +void mitk::AbstractGlobalImageFeature::InitializeQuantifierFromParameters(const Image::Pointer & feature, const Image::Pointer &mask, unsigned int defaultBins) +{ + unsigned int bins = 0; + double binsize = 0; + double minimum = 0; + double maximum = 0; + + auto parsedArgs = GetParameter(); + std::string name = GetOptionPrefix(); + + bool useGlobal = true; + if (parsedArgs.count(name + "::ignore-global-histogram")) + { + useGlobal = false; + SetUseMinimumIntensity(false); + SetUseMaximumIntensity(false); + SetUseBinsize(false); + SetUseBins(false); + } + + if (useGlobal) + { + if (parsedArgs.count("ignore-mask-for-histogram")) + { + bool tmp = us::any_cast(parsedArgs["ignore-mask-for-histogram"]); + SetIgnoreMask(tmp); + } + if (parsedArgs.count("minimum-intensity")) + { + minimum = us::any_cast(parsedArgs["minimum-intensity"]); + SetMinimumIntensity(minimum); + SetUseMinimumIntensity(true); + } + if (parsedArgs.count("maximum-intensity")) + { + maximum = us::any_cast(parsedArgs["maximum-intensity"]); + SetMaximumIntensity(maximum); + SetUseMaximumIntensity(true); + } + if (parsedArgs.count("bins")) + { + bins = us::any_cast(parsedArgs["bins"]); + SetBins(bins); + SetUseBins(true); + } + if (parsedArgs.count("binsize")) + { + binsize = us::any_cast(parsedArgs["binsize"]); + SetBinsize(binsize); + SetUseBinsize(true); + } + } + if (parsedArgs.count(name+"::ignore-mask-for-histogram")) + { + bool tmp = us::any_cast(parsedArgs[name+"::ignore-mask-for-histogram"]); + SetIgnoreMask(tmp); + } + if (parsedArgs.count(name + "::minimum")) + { + minimum = us::any_cast(parsedArgs[name + "::minimum"]); + SetMinimumIntensity(minimum); + SetUseMinimumIntensity(true); + } + if (parsedArgs.count(name + "::maximum")) + { + maximum = us::any_cast(parsedArgs[name + "::maximum"]); + SetMaximumIntensity(maximum); + SetUseMaximumIntensity(true); + } + if (parsedArgs.count(name + "::bins")) + { + bins = us::any_cast(parsedArgs[name + "::bins"]); + SetBins(bins); + } + if (parsedArgs.count(name + "::binsize")) + { + binsize = us::any_cast(parsedArgs[name + "::binsize"]); + SetBinsize(binsize); + SetUseBinsize(true); + } + InitializeQuantifier(feature, mask, defaultBins); +} + +void mitk::AbstractGlobalImageFeature::InitializeQuantifier(const Image::Pointer & feature, const Image::Pointer &mask, unsigned int defaultBins) +{ + MITK_INFO << GetUseMinimumIntensity() << " " << GetUseMaximumIntensity() << " " << GetUseBins() << " " << GetUseBinsize(); + + m_Quantifier = IntensityQuantifier::New(); + if (GetUseMinimumIntensity() && GetUseMaximumIntensity() && GetUseBinsize()) + m_Quantifier->InitializeByBinsizeAndMaximum(GetMinimumIntensity(), GetMaximumIntensity(), GetBinsize()); + else if (GetUseMinimumIntensity() && GetUseBins() && GetUseBinsize()) + m_Quantifier->InitializeByBinsizeAndBins(GetMinimumIntensity(), GetBins(), GetBinsize()); + else if (GetUseMinimumIntensity() && GetUseMaximumIntensity() && GetUseBins()) + m_Quantifier->InitializeByMinimumMaximum(GetMinimumIntensity(), GetMaximumIntensity(), GetBins()); + // Intialize from Image and Binsize + else if (GetUseBinsize() && GetIgnoreMask() && GetUseMinimumIntensity()) + m_Quantifier->InitializeByImageAndBinsizeAndMinimum(feature, GetMinimumIntensity(), GetBinsize()); + else if (GetUseBinsize() && GetIgnoreMask() && GetUseMaximumIntensity()) + m_Quantifier->InitializeByImageAndBinsizeAndMaximum(feature, GetMaximumIntensity(), GetBinsize()); + else if (GetUseBinsize() && GetIgnoreMask()) + m_Quantifier->InitializeByImageAndBinsize(feature, GetBinsize()); + // Initialize form Image, Mask and Binsize + else if (GetUseBinsize() && GetUseMinimumIntensity()) + m_Quantifier->InitializeByImageRegionAndBinsizeAndMinimum(feature, mask, GetMinimumIntensity(), GetBinsize()); + else if (GetUseBinsize() && GetUseMaximumIntensity()) + m_Quantifier->InitializeByImageRegionAndBinsizeAndMaximum(feature, mask, GetMaximumIntensity(), GetBinsize()); + else if (GetUseBinsize()) + m_Quantifier->InitializeByImageRegionAndBinsize(feature, mask, GetBinsize()); + // Intialize from Image and Bins + else if (GetUseBins() && GetIgnoreMask() && GetUseMinimumIntensity()) + m_Quantifier->InitializeByImageAndMinimum(feature, GetMinimumIntensity(), GetBins()); + else if (GetUseBins() && GetIgnoreMask() && GetUseMaximumIntensity()) + m_Quantifier->InitializeByImageAndMaximum(feature, GetMaximumIntensity(), GetBins()); + else if (GetUseBins()) + m_Quantifier->InitializeByImage(feature, GetBins()); + // Intialize from Image, Mask and Bins + else if (GetUseBins() && GetUseMinimumIntensity()) + m_Quantifier->InitializeByImageRegionAndMinimum(feature, mask, GetMinimumIntensity(), GetBins()); + else if (GetUseBins() && GetUseMaximumIntensity()) + m_Quantifier->InitializeByImageRegionAndMaximum(feature, mask, GetMaximumIntensity(), GetBins()); + else if (GetUseBins()) + m_Quantifier->InitializeByImageRegion(feature, mask, GetBins()); + // Default + else if (GetIgnoreMask()) + m_Quantifier->InitializeByImage(feature, GetBins()); + else + m_Quantifier->InitializeByImageRegion(feature, mask, defaultBins); +} + +std::string mitk::AbstractGlobalImageFeature::GetCurrentFeatureEncoding() +{ + return ""; +} + +std::string mitk::AbstractGlobalImageFeature::FeatureDescriptionPrefix() +{ + std::string output; + output = m_FeatureClassName + "::"; + if (m_EncodeParameters) + { + output += GetCurrentFeatureEncoding() + "::"; + } + return output; +} + +std::string mitk::AbstractGlobalImageFeature::QuantifierParameterString() +{ + std::stringstream ss; + if (GetUseMinimumIntensity() && GetUseMaximumIntensity() && GetUseBinsize()) + ss << "Min-" << GetMinimumIntensity() << "_Max-" << GetMaximumIntensity() << "_BS-" << GetBinsize(); + else if (GetUseMinimumIntensity() && GetUseBins() && GetUseBinsize()) + ss << "Min-" << GetMinimumIntensity() << "_Bins-" << GetBins() << "_BS-" << GetBinsize(); + else if (GetUseMinimumIntensity() && GetUseMaximumIntensity() && GetUseBins()) + ss << "Min-" << GetMinimumIntensity() << "_Max-" << GetMaximumIntensity() << "_Bins-" << GetBins(); + // Intialize from Image and Binsize + else if (GetUseBinsize() && GetIgnoreMask() && GetUseMinimumIntensity()) + ss << "Min-" << GetMinimumIntensity() << "_BS-" << GetBinsize() << "_FullImage"; + else if (GetUseBinsize() && GetIgnoreMask() && GetUseMaximumIntensity()) + ss << "Max-" << GetMaximumIntensity() << "_BS-" << GetBinsize() << "_FullImage"; + else if (GetUseBinsize() && GetIgnoreMask()) + ss << "BS-" << GetBinsize() << "_FullImage"; + // Initialize form Image, Mask and Binsize + else if (GetUseBinsize() && GetUseMinimumIntensity()) + ss << "Min-" << GetMinimumIntensity() << "_BS-" << GetBinsize(); + else if (GetUseBinsize() && GetUseMaximumIntensity()) + ss << "Max-" << GetMaximumIntensity() << "_BS-" << GetBinsize(); + else if (GetUseBinsize()) + ss << "BS-" << GetBinsize(); + // Intialize from Image and Bins + else if (GetUseBins() && GetIgnoreMask() && GetUseMinimumIntensity()) + ss << "Min-" << GetMinimumIntensity() << "_Bins-" << GetBins() << "_FullImage"; + else if (GetUseBins() && GetIgnoreMask() && GetUseMaximumIntensity()) + ss << "Max-" << GetMaximumIntensity() << "_Bins-" << GetBins() << "_FullImage"; + else if (GetUseBins()) + ss << "Bins-" << GetBins() << "_FullImage"; + // Intialize from Image, Mask and Bins + else if (GetUseBins() && GetUseMinimumIntensity()) + ss << "Min-" << GetMinimumIntensity() << "_Bins-" << GetBins(); + else if (GetUseBins() && GetUseMaximumIntensity()) + ss << "Max-" << GetMaximumIntensity() << "_Bins-" << GetBins(); + else if (GetUseBins()) + ss << "Bins-" << GetBins(); + // Default + else if (GetIgnoreMask()) + ss << "Bins-" << GetBins() << "_FullImage"; + else + ss << "Bins-" << GetBins(); + return ss.str(); +} \ No newline at end of file diff --git a/Modules/Classification/CLCore/src/mitkIntensityQuantifier.cpp b/Modules/Classification/CLCore/src/mitkIntensityQuantifier.cpp new file mode 100644 index 0000000000..cbe0ec2536 --- /dev/null +++ b/Modules/Classification/CLCore/src/mitkIntensityQuantifier.cpp @@ -0,0 +1,199 @@ +/*=================================================================== + +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 + +// STD +#include + +// ITK +#include + +// MITK +#include +#include + +template +static void +CalculateImageMinMax(itk::Image* itkImage, double &minimum, double &maximum) +{ + typedef itk::Image ImageType; + + minimum = std::numeric_limits::max(); + maximum = std::numeric_limits::lowest(); + + itk::ImageRegionConstIterator iter(itkImage, itkImage->GetLargestPossibleRegion()); + + while (!iter.IsAtEnd()) + { + minimum = std::min(minimum, iter.Get()); + maximum = std::max(maximum, iter.Get()); + ++iter; + } +} + +template +static void +CalculateImageRegionMinMax(itk::Image* itkImage, mitk::Image::Pointer mask, double &minimum, double &maximum) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskType; + + typename MaskType::Pointer itkMask = MaskType::New(); + mitk::CastToItkImage(mask, itkMask); + + minimum = std::numeric_limits::max(); + maximum = std::numeric_limits::lowest(); + + itk::ImageRegionConstIterator iter(itkImage, itkImage->GetLargestPossibleRegion()); + itk::ImageRegionConstIterator maskIter(itkMask, itkMask->GetLargestPossibleRegion()); + + while (!iter.IsAtEnd()) + { + if (maskIter.Get() > 0) + { + minimum = std::min(minimum, iter.Get()); + maximum = std::max(maximum, iter.Get()); + } + ++iter; + ++maskIter; + } +} + +mitk::IntensityQuantifier::IntensityQuantifier() : + m_Initialized(false), + m_Bins(0), + m_Binsize(0), + m_Minimum(0), + m_Maximum(0) +{} + +void mitk::IntensityQuantifier::InitializeByMinimumMaximum(double minimum, double maximum, unsigned int bins) { + m_Minimum = minimum; + m_Maximum = maximum; + m_Bins = bins; + m_Binsize = (maximum - minimum) / bins; + m_Initialized = true; +} + +void mitk::IntensityQuantifier::InitializeByBinsizeAndBins(double minimum, unsigned int bins, double binsize) { + m_Minimum = minimum; + m_Maximum = minimum + bins*binsize; + m_Bins = bins; + m_Binsize = binsize; + m_Initialized = true; +} + +void mitk::IntensityQuantifier::InitializeByBinsizeAndMaximum(double minimum, double maximum, double binsize) { + m_Minimum = minimum; + m_Bins = std::ceil((maximum - minimum) / binsize); + m_Maximum = minimum + m_Bins*binsize; + m_Binsize = binsize; + m_Initialized = true; +} + +void mitk::IntensityQuantifier::InitializeByImage(mitk::Image::Pointer image, unsigned int bins) { + double minimum, maximum; + AccessByItk_2(image, CalculateImageMinMax, minimum, maximum); + InitializeByMinimumMaximum(minimum, maximum, bins); +} + +void mitk::IntensityQuantifier::InitializeByImageAndMinimum(mitk::Image::Pointer image, double minimum, unsigned int bins) { + double tmp, maximum; + AccessByItk_2(image, CalculateImageMinMax, tmp, maximum); + InitializeByMinimumMaximum(minimum, maximum, bins); +} + +void mitk::IntensityQuantifier::InitializeByImageAndMaximum(mitk::Image::Pointer image, double maximum, unsigned int bins) { + double minimum, tmp; + AccessByItk_2(image, CalculateImageMinMax, minimum, tmp); + InitializeByMinimumMaximum(minimum, maximum, bins); +} + +void mitk::IntensityQuantifier::InitializeByImageRegion(mitk::Image::Pointer image, mitk::Image::Pointer mask, unsigned int bins) { + double minimum, maximum; + AccessByItk_3(image, CalculateImageRegionMinMax, mask, minimum, maximum); + InitializeByMinimumMaximum(minimum, maximum, bins); +} + +void mitk::IntensityQuantifier::InitializeByImageRegionAndMinimum(mitk::Image::Pointer image, mitk::Image::Pointer mask, double minimum, unsigned int bins) { + double tmp, maximum; + AccessByItk_3(image, CalculateImageRegionMinMax, mask, tmp, maximum); + InitializeByMinimumMaximum(minimum, maximum, bins); +} + +void mitk::IntensityQuantifier::InitializeByImageRegionAndMaximum(mitk::Image::Pointer image, mitk::Image::Pointer mask, double maximum, unsigned int bins) { + double minimum, tmp; + AccessByItk_3(image, CalculateImageRegionMinMax, mask, minimum, tmp); + InitializeByMinimumMaximum(minimum, maximum, bins); +} + +void mitk::IntensityQuantifier::InitializeByImageAndBinsize(mitk::Image::Pointer image, double binsize) { + double minimum, maximum; + AccessByItk_2(image, CalculateImageMinMax, minimum, maximum); + InitializeByBinsizeAndMaximum(minimum, maximum, binsize); +} + +void mitk::IntensityQuantifier::InitializeByImageAndBinsizeAndMinimum(mitk::Image::Pointer image, double minimum, double binsize) { + double tmp, maximum; + AccessByItk_2(image, CalculateImageMinMax, tmp, maximum); + InitializeByBinsizeAndMaximum(minimum, maximum, binsize); +} + +void mitk::IntensityQuantifier::InitializeByImageAndBinsizeAndMaximum(mitk::Image::Pointer image, double maximum, double binsize) { + double minimum, tmp; + AccessByItk_2(image, CalculateImageMinMax, minimum, tmp); + InitializeByBinsizeAndMaximum(minimum, maximum, binsize); +} + +void mitk::IntensityQuantifier::InitializeByImageRegionAndBinsize(mitk::Image::Pointer image, mitk::Image::Pointer mask, double binsize) { + double minimum, maximum; + AccessByItk_3(image, CalculateImageRegionMinMax, mask, minimum, maximum); + InitializeByBinsizeAndMaximum(minimum, maximum, binsize); +} + +void mitk::IntensityQuantifier::InitializeByImageRegionAndBinsizeAndMinimum(mitk::Image::Pointer image, mitk::Image::Pointer mask, double minimum, double binsize) { + double tmp, maximum; + AccessByItk_3(image, CalculateImageRegionMinMax, mask, tmp, maximum); + InitializeByBinsizeAndMaximum(minimum, maximum, binsize); +} + +void mitk::IntensityQuantifier::InitializeByImageRegionAndBinsizeAndMaximum(mitk::Image::Pointer image, mitk::Image::Pointer mask, double maximum, double binsize) { + double minimum, tmp; + AccessByItk_3(image, CalculateImageRegionMinMax, mask, minimum, tmp); + InitializeByBinsizeAndMaximum(minimum, maximum, binsize); +} + +unsigned int mitk::IntensityQuantifier::IntensityToIndex(double intensity) +{ + double index = std::floor((intensity - m_Minimum) / m_Binsize); + return std::max(0, std::min(index, m_Bins-1)); +} + +double mitk::IntensityQuantifier::IndexToMinimumIntensity(unsigned int index) +{ + return index*m_Binsize + m_Minimum; +} + +double mitk::IntensityQuantifier::IndexToMeanIntensity(unsigned int index) +{ + return (index + 0.5) * m_Binsize + m_Minimum; +} + +double mitk::IntensityQuantifier::IndexToMaximumIntensity(unsigned int index) +{ + return (index + 1) * m_Binsize + m_Minimum; +} diff --git a/Modules/Classification/CLImportanceWeighting/src/mitkGeneralizedLinearModel.cpp b/Modules/Classification/CLImportanceWeighting/src/mitkGeneralizedLinearModel.cpp index 2e5f0c7476..d8f64013ad 100644 --- a/Modules/Classification/CLImportanceWeighting/src/mitkGeneralizedLinearModel.cpp +++ b/Modules/Classification/CLImportanceWeighting/src/mitkGeneralizedLinearModel.cpp @@ -1,283 +1,283 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include static void _UpdateXMatrix(const vnl_matrix &xData, bool addConstant, v3p_netlib_doublereal *x); static void _UpdatePermXMatrix(const vnl_matrix &xData, bool addConstant, const vnl_vector &permutation, vnl_matrix &x); static void _InitMuEta(mitk::DistSimpleBinominal *dist, mitk::LogItLinking *link, const vnl_vector &yData, vnl_vector &mu, vnl_vector &eta); static void _FinalizeBVector(vnl_vector &b, vnl_vector &perm, int cols); double mitk::GeneralizedLinearModel::Predict( const vnl_vector &c) { LogItLinking link; double mu = 0; int cols = m_B.size(); for (int i = 0; i < cols; ++i) { if (!m_AddConstantColumn) mu += c(i)* m_B(i); else if ( i == 0) mu += 1* m_B(i); else mu += c(i-1)*m_B(i); } return link.InverseLink(mu); } vnl_vector mitk::GeneralizedLinearModel::Predict(const vnl_matrix &x) { LogItLinking link; vnl_vector mu(x.rows()); int cols = m_B.size(); for (unsigned int r = 0 ; r < mu.size(); ++r) { mu(r) = 0; for (int c = 0; c < cols; ++c) { if (!m_AddConstantColumn) mu(r) += x(r,c)*m_B(c); else if ( c == 0) mu(r) += m_B(c); else mu(r) += x(r,c-1)*m_B(c); } mu(r) = link.InverseLink(mu(r)); } return mu; } vnl_vector mitk::GeneralizedLinearModel::B() { return m_B; } vnl_vector mitk::GeneralizedLinearModel::ExpMu(const vnl_matrix &x) { LogItLinking link; vnl_vector mu(x.rows()); int cols = m_B.size(); for (unsigned int r = 0 ; r < mu.size(); ++r) { mu(r) = 0; for (int c = 0; c < cols; ++c) { if (!m_AddConstantColumn) mu(r) += x(r,c)*m_B(c); else if ( c == 0) mu(r) += m_B(c); else mu(r) += x(r,c-1)*m_B(c); } mu(r) = exp(-mu(r)); } return mu; } mitk::GeneralizedLinearModel::GeneralizedLinearModel(const vnl_matrix &xData, const vnl_vector &yData, bool addConstantColumn) : m_AddConstantColumn(addConstantColumn) { EstimatePermutation(xData); DistSimpleBinominal dist; LogItLinking link; vnl_matrix x; int rows = xData.rows(); int cols = m_Permutation.size(); vnl_vector mu(rows); vnl_vector eta(rows); vnl_vector weightedY(rows); vnl_matrix weightedX(rows, cols); vnl_vector oldB(cols); _UpdatePermXMatrix(xData, m_AddConstantColumn, m_Permutation, x); _InitMuEta(&dist, &link, yData, mu, eta); int iter = 0; int iterLimit = 100; double sqrtEps = sqrt(std::numeric_limits::epsilon()); double convertCriterion =1e-6; m_B.set_size(m_Permutation.size()); m_B.fill(0); while (iter <= iterLimit) { ++iter; oldB = m_B; - // Do Row-wise operation. No Vector oepration at this point. + // Do Row-wise operation. No Vector operation at this point. for (int r = 0; r qr(weightedX); m_B = qr.solve(weightedY); eta = x * m_B; for (int r = 0; r < rows; ++r) { mu(r) = link.InverseLink(eta(r)); } bool stayInLoop = false; for(int c= 0; c < cols; ++c) { stayInLoop |= std::abs( m_B(c) - oldB(c)) > convertCriterion * std::max(sqrtEps, std::abs(oldB(c))); } if (!stayInLoop) break; } _FinalizeBVector(m_B, m_Permutation, xData.cols()); } void mitk::GeneralizedLinearModel::EstimatePermutation(const vnl_matrix &xData) { v3p_netlib_integer rows = xData.rows(); v3p_netlib_integer cols = xData.cols(); if (m_AddConstantColumn) ++cols; v3p_netlib_doublereal *x = new v3p_netlib_doublereal[rows* cols]; _UpdateXMatrix(xData, m_AddConstantColumn, x); v3p_netlib_doublereal *qraux = new v3p_netlib_doublereal[cols]; v3p_netlib_integer *jpvt = new v3p_netlib_integer[cols]; std::fill_n(jpvt,cols,0); v3p_netlib_doublereal *work = new v3p_netlib_doublereal[cols]; std::fill_n(work,cols,0); v3p_netlib_integer job = 16; // Make a call to Lapack-DQRDC which does QR with permutation // Permutation is saved in JPVT. v3p_netlib_dqrdc_(x, &rows, &rows, &cols, qraux, jpvt, work, &job); double limit = std::abs(x[0]) * std::max(cols, rows) * std::numeric_limits::epsilon(); // Calculate the rank of the matrix int m_Rank = 0; for (int i = 0; i limit) ? 1 : 0; } // Create a permutation vector m_Permutation.set_size(m_Rank); for (int i = 0; i < m_Rank; ++i) { m_Permutation(i) = jpvt[i]-1; } delete[] x; delete[] qraux; delete[] jpvt; delete[] work; } // Copy a vnl-matrix to an c-array with row-wise representation. // Adds a constant column if required. static void _UpdateXMatrix(const vnl_matrix &xData, bool addConstant, v3p_netlib_doublereal *x) { v3p_netlib_integer rows = xData.rows(); v3p_netlib_integer cols = xData.cols(); if (addConstant) ++cols; for (int r=0; r < rows; ++r) { for (int c=0; c &xData, bool addConstant, const vnl_vector &permutation, vnl_matrix &x) { int rows = xData.rows(); int cols = permutation.size(); x.set_size(rows, cols); for (int r=0; r < rows; ++r) { for (int c=0; c &yData, vnl_vector &mu, vnl_vector &eta) { int rows = yData.size(); mu.set_size(rows); eta.set_size(rows); for (int r = 0; r < rows; ++r) { mu(r) = dist->Init(yData(r)); eta(r) = link->Link(mu(r)); } } // Inverts the permutation on a given b-vector. // Necessary to get a b-vector that match the original data static void _FinalizeBVector(vnl_vector &b, vnl_vector &perm, int cols) { vnl_vector tempB(cols+1); tempB.fill(0); for (unsigned int c = 0; c < perm.size(); ++c) { tempB(perm(c)) = b(c); } b = tempB; } diff --git a/Modules/Classification/CLMRUtilities/include/mitkMRNormLinearStatisticBasedFilter.h b/Modules/Classification/CLMRUtilities/include/mitkMRNormLinearStatisticBasedFilter.h index ed13140b3b..dc4adf251b 100644 --- a/Modules/Classification/CLMRUtilities/include/mitkMRNormLinearStatisticBasedFilter.h +++ b/Modules/Classification/CLMRUtilities/include/mitkMRNormLinearStatisticBasedFilter.h @@ -1,71 +1,85 @@ /*=================================================================== 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 MRNORMLINEARSTATISTICBASEDFILTER_H #define MRNORMLINEARSTATISTICBASEDFILTER_H #include "mitkCommon.h" #include "MitkCLMRUtilitiesExports.h" #include "mitkImageToImageFilter.h" #include "mitkImageTimeSelector.h" #include "itkImage.h" namespace mitk { //##Documentation //## @brief //## @ingroup Process class MITKCLMRUTILITIES_EXPORT MRNormLinearStatisticBasedFilter : public ImageToImageFilter { public: mitkClassMacro(MRNormLinearStatisticBasedFilter, ImageToImageFilter); itkFactorylessNewMacro(Self); itkCloneMacro(Self); void SetMask( const mitk::Image* mask ); const mitk::Image* GetMask() const; enum NormalizationBase { MEAN, MODE, MEDIAN }; itkGetConstMacro(CenterMode, NormalizationBase); itkSetMacro(CenterMode, NormalizationBase); + itkGetConstMacro(IgnoreOutlier, bool); + itkSetMacro(IgnoreOutlier, bool); + + itkGetConstMacro(TargetValue, double); + itkSetMacro(TargetValue, double); + + itkGetConstMacro(TargetWidth, double); + itkSetMacro(TargetWidth, double); + protected: MRNormLinearStatisticBasedFilter(); ~MRNormLinearStatisticBasedFilter() override; void GenerateInputRequestedRegion() override; void GenerateOutputInformation() override; void GenerateData() override; template < typename TPixel, unsigned int VImageDimension > void InternalComputeMask(itk::Image* itkImage); NormalizationBase m_CenterMode; + bool m_IgnoreOutlier; + private: + double m_TargetValue; + double m_TargetWidth; + }; } // namespace mitk #endif /* MRNORMLINEARSTATISTICBASEDFILTER_H */ diff --git a/Modules/Classification/CLMRUtilities/src/MRNormalization/mitkMRNormLinearStatisticBasedFilter.cpp b/Modules/Classification/CLMRUtilities/src/MRNormalization/mitkMRNormLinearStatisticBasedFilter.cpp index b68bbccded..b22445c8c9 100644 --- a/Modules/Classification/CLMRUtilities/src/MRNormalization/mitkMRNormLinearStatisticBasedFilter.cpp +++ b/Modules/Classification/CLMRUtilities/src/MRNormalization/mitkMRNormLinearStatisticBasedFilter.cpp @@ -1,147 +1,173 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMRNormLinearStatisticBasedFilter.h" #include "mitkImageToItk.h" #include "mitkImageAccessByItk.h" #include "itkImageRegionIterator.h" // MITK #include #include #include // ITK #include #include mitk::MRNormLinearStatisticBasedFilter::MRNormLinearStatisticBasedFilter() : - m_CenterMode(MRNormLinearStatisticBasedFilter::MEDIAN) +m_CenterMode(MRNormLinearStatisticBasedFilter::MEDIAN), m_TargetValue(0), m_TargetWidth(1) { - this->SetNumberOfIndexedInputs(3); - this->SetNumberOfRequiredInputs(3); + this->SetNumberOfIndexedInputs(2); + this->SetNumberOfRequiredInputs(1); } mitk::MRNormLinearStatisticBasedFilter::~MRNormLinearStatisticBasedFilter() { } void mitk::MRNormLinearStatisticBasedFilter::SetMask( const mitk::Image* mask ) { // Process object is not const-correct so the const_cast is required here auto* nonconstMask = const_cast< mitk::Image * >( mask ); this->SetNthInput(1, nonconstMask ); } const mitk::Image* mitk::MRNormLinearStatisticBasedFilter::GetMask() const { return this->GetInput(1); } void mitk::MRNormLinearStatisticBasedFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); mitk::Image* input = this->GetInput(); input->SetRequestedRegionToLargestPossibleRegion(); } void mitk::MRNormLinearStatisticBasedFilter::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); - itkDebugMacro(<<"GenerateOutputInformation()"); + itkDebugMacro(<< "GenerateOutputInformation()"); output->Initialize(input->GetPixelType(), *input->GetTimeGeometry()); output->SetPropertyList(input->GetPropertyList()->Clone()); } template < typename TPixel, unsigned int VImageDimension > void mitk::MRNormLinearStatisticBasedFilter::InternalComputeMask(itk::Image* itkImage) { // Define all necessary Types typedef itk::Image ImageType; typedef itk::Image MaskType; typedef itk::LabelStatisticsImageFilter FilterType; typedef itk::MinimumMaximumImageCalculator MinMaxComputerType; typename MaskType::Pointer itkMask0 = MaskType::New(); mitk::CastToItkImage(this->GetMask(), itkMask0); typename ImageType::Pointer outImage = ImageType::New(); mitk::CastToItkImage(this->GetOutput(0), outImage); typename MinMaxComputerType::Pointer minMaxComputer = MinMaxComputerType::New(); minMaxComputer->SetImage(itkImage); minMaxComputer->Compute(); + double min = minMaxComputer->GetMinimum(); + double max = minMaxComputer->GetMaximum(); + + if (m_IgnoreOutlier) + { + typename FilterType::Pointer labelStatisticsImageFilter = FilterType::New(); + labelStatisticsImageFilter->SetUseHistograms(true); + labelStatisticsImageFilter->SetHistogramParameters(2048, min, max); + labelStatisticsImageFilter->SetInput(itkImage); + + labelStatisticsImageFilter->SetLabelInput(itkMask0); + labelStatisticsImageFilter->Update(); + auto histo = labelStatisticsImageFilter->GetHistogram(1); + min = histo->Quantile(0, 0.02); + max = histo->Quantile(0, 0.98); + } + typename FilterType::Pointer labelStatisticsImageFilter = FilterType::New(); labelStatisticsImageFilter->SetUseHistograms(true); - labelStatisticsImageFilter->SetHistogramParameters(256, minMaxComputer->GetMinimum(),minMaxComputer->GetMaximum()); + labelStatisticsImageFilter->SetHistogramParameters(256, min, max); labelStatisticsImageFilter->SetInput( itkImage ); labelStatisticsImageFilter->SetLabelInput(itkMask0); labelStatisticsImageFilter->Update(); double median0 = labelStatisticsImageFilter->GetMedian(1); double mean0 = labelStatisticsImageFilter->GetMean(1); double stddev = labelStatisticsImageFilter->GetSigma(1); double modulo0=0; { auto histo = labelStatisticsImageFilter->GetHistogram(1); double maxFrequency=0; for (auto hIter=histo->Begin();hIter!=histo->End();++hIter) { if (maxFrequency < hIter.GetFrequency()) { maxFrequency = hIter.GetFrequency(); modulo0 = (histo->GetBinMin(0,hIter.GetInstanceIdentifier()) + histo->GetBinMax(0,hIter.GetInstanceIdentifier())) / 2.0; } } } double value0=0; switch (m_CenterMode) { case MRNormLinearStatisticBasedFilter::MEAN: value0=mean0; break; case MRNormLinearStatisticBasedFilter::MEDIAN: value0=median0; break; case MRNormLinearStatisticBasedFilter::MODE: value0=modulo0; break; } - double offset = value0; - double scaling = stddev; + double offset = value0+m_TargetValue; + double scaling = stddev*m_TargetWidth; if (scaling < 0.0001) return; itk::ImageRegionIterator inIter(itkImage, itkImage->GetLargestPossibleRegion()); itk::ImageRegionIterator outIter(outImage, outImage->GetLargestPossibleRegion()); while (! inIter.IsAtEnd()) { TPixel value = inIter.Value(); + if (m_IgnoreOutlier && (value < min)) + { + value = min; + } + else if (m_IgnoreOutlier && (value > max)) + { + value = max; + } + outIter.Set((value - offset) / scaling); ++inIter; ++outIter; } } void mitk::MRNormLinearStatisticBasedFilter::GenerateData() { AccessByItk(GetInput(0),InternalComputeMask); } \ No newline at end of file diff --git a/Modules/Classification/CLMiniApps/CL2Dto3DImage.cpp b/Modules/Classification/CLMiniApps/CL2Dto3DImage.cpp new file mode 100644 index 0000000000..66e1acaa53 --- /dev/null +++ b/Modules/Classification/CLMiniApps/CL2Dto3DImage.cpp @@ -0,0 +1,59 @@ +/*=================================================================== + +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 "mitkCommandLineParser.h" +#include +#include "mitkIOUtil.h" + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + + parser.setTitle("Dicom Loader"); + parser.setCategory("Preprocessing Tools"); + parser.setDescription(""); + parser.setContributor("MBI"); + + parser.setArgumentPrefix("--","-"); + // Add command line argument names + parser.addArgument("help", "h",mitkCommandLineParser::Bool, "Help:", "Show this help text"); + parser.addArgument("input", "i", mitkCommandLineParser::InputDirectory, "Input file:", "Input file",us::Any(),false); + parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output file:", "Output file",us::Any(),false); + + std::map parsedArgs = parser.parseArguments(argc, argv); + + if (parsedArgs.size()==0) + return EXIT_FAILURE; + + // Show a help message + if ( parsedArgs.count("help") || parsedArgs.count("h")) + { + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + + std::string inputName = us::any_cast(parsedArgs["input"]); + std::string outputName = us::any_cast(parsedArgs["output"]); + + mitk::Image::Pointer image = mitk::IOUtil::LoadImage(inputName); + mitk::Convert2Dto3DImageFilter::Pointer multiFilter2 = mitk::Convert2Dto3DImageFilter::New(); + multiFilter2->SetInput(image); + multiFilter2->Update(); + mitk::Image::Pointer image2 = multiFilter2->GetOutput(); + mitk::IOUtil::Save(image2, outputName); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/Modules/Classification/CLMiniApps/CLDicom2Nrrd.cpp b/Modules/Classification/CLMiniApps/CLDicom2Nrrd.cpp index 1712514acd..490fc4df87 100644 --- a/Modules/Classification/CLMiniApps/CLDicom2Nrrd.cpp +++ b/Modules/Classification/CLMiniApps/CLDicom2Nrrd.cpp @@ -1,87 +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. ===================================================================*/ #include "mitkDicomSeriesReader.h" #include "mitkProperties.h" #include "mitkCommandLineParser.h" #include "mitkIOUtil.h" +#include "mitkPreferenceListReaderOptionsFunctor.h" + int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setTitle("Dicom Loader"); parser.setCategory("Preprocessing Tools"); parser.setDescription(""); parser.setContributor("MBI"); parser.setArgumentPrefix("--","-"); // Add command line argument names parser.addArgument("help", "h",mitkCommandLineParser::Bool, "Help:", "Show this help text"); parser.addArgument("input", "i", mitkCommandLineParser::InputDirectory, "Input folder:", "Input folder",us::Any(),false); - parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output file:", "Output file",us::Any(),false); + parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output file:", "Output file", us::Any(), false); + parser.addArgument("reader", "r", mitkCommandLineParser::String, "Reader Name", "Reader Name", us::Any()); std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) return EXIT_FAILURE; // Show a help message if ( parsedArgs.count("help") || parsedArgs.count("h")) { std::cout << parser.helpText(); return EXIT_SUCCESS; } std::string inputFolder = us::any_cast(parsedArgs["input"]); std::string outFileName = us::any_cast(parsedArgs["output"]); - //check if DICOMTags have been set as property for mitk::Image - mitk::DicomSeriesReader::FileNamesGrouping seriesInFiles = mitk::DicomSeriesReader::GetSeries( inputFolder, true ); - std::list images; - std::map fileMap; - - // TODO sort series UIDs, implementation of map iterator might differ on different platforms (or verify this is a standard topic??) - for (mitk::DicomSeriesReader::FileNamesGrouping::const_iterator seriesIter = seriesInFiles.begin(); - seriesIter != seriesInFiles.end(); - ++seriesIter) + //mitk::PreferenceListReaderOptionsFunctor::ListType preference = { "MITK DICOM Reader v2 (classic config)" }; + mitk::PreferenceListReaderOptionsFunctor::ListType preference = {}; + if (parsedArgs.count("reader")) { - mitk::DicomSeriesReader::StringContainer files = seriesIter->second.GetFilenames(); + preference.push_back(us::any_cast(parsedArgs["reader"])); + } + mitk::PreferenceListReaderOptionsFunctor::ListType emptyList = {}; + mitk::IOUtil::LoadInfo info(inputFolder); + mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(preference, emptyList); + functor(info); - mitk::DataNode::Pointer node = mitk::DicomSeriesReader::LoadDicomSeries( files ); + std::string extension = itksys::SystemTools::GetFilenameExtension(outFileName); + std::string filename = itksys::SystemTools::GetFilenameWithoutExtension(outFileName); + std::string path = itksys::SystemTools::GetFilenamePath(outFileName); - if (node.IsNotNull()) - { - mitk::Image::Pointer image = dynamic_cast( node->GetData() ); + auto nodes = mitk::IOUtil::Load(inputFolder, &functor); - images.push_back( image ); - fileMap.insert( std::pair(image,files)); + unsigned count = 0; + for (auto node : nodes) + { + std::string writeName = path + "/" + filename + extension; + if (count > 0) + { + writeName = path + "/" + filename + "_" + std::to_string(count) + extension; } + mitk::IOUtil::Save(node, writeName); + ++count; } - // WARN: EXPECT ONLY ONE ITEM PER FOLDER - for ( std::list::const_iterator imageIter = images.begin(); - imageIter != images.end(); - ++imageIter ) - { - const mitk::Image::Pointer image = *imageIter; - mitk::IOUtil::Save(image,outFileName); - } return EXIT_SUCCESS; } diff --git a/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp b/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp index ac3ccc346e..b16e57dd7f 100644 --- a/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp +++ b/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp @@ -1,212 +1,758 @@ /*=================================================================== 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 mitkCLPolyToNrrd_cpp #define mitkCLPolyToNrrd_cpp #include "time.h" #include #include #include #include "mitkCommandLineParser.h" +#include +#include + #include -#include +#include +#include #include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + + +#include "itkNearestNeighborInterpolateImageFunction.h" +#include "itkResampleImageFilter.h" + +#include +#include +#include "QmitkRegisterClasses.h" +#include "QmitkRenderWindow.h" +#include "vtkRenderLargeImage.h" +#include "vtkPNGWriter.h" + + typedef itk::Image< double, 3 > FloatImageType; -typedef itk::Image< unsigned char, 3 > MaskImageType; +typedef itk::Image< unsigned short, 3 > MaskImageType; + +template +class punct_facet : public std::numpunct { +public: + punct_facet(charT sep) : + m_Sep(sep) + { + + } +protected: + charT do_decimal_point() const { return m_Sep; } +private: + charT m_Sep; +}; + +template +void +ResampleImage(itk::Image* itkImage, float resolution, mitk::Image::Pointer& newImage) +{ + typedef itk::Image ImageType; + typedef itk::ResampleImageFilter ResampleFilterType; + + typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); + auto spacing = itkImage->GetSpacing(); + auto size = itkImage->GetLargestPossibleRegion().GetSize(); + + for (unsigned int i = 0; i < VImageDimension; ++i) + { + size[i] = size[i] / (1.0*resolution)*(1.0*spacing[i])+1.0; + } + spacing.Fill(resolution); + + resampler->SetInput(itkImage); + resampler->SetSize(size); + resampler->SetOutputSpacing(spacing); + resampler->SetOutputOrigin(itkImage->GetOrigin()); + resampler->SetOutputDirection(itkImage->GetDirection()); + resampler->Update(); + + newImage->InitializeByItk(resampler->GetOutput()); + mitk::GrabItkImageMemory(resampler->GetOutput(), newImage); +} + + +template +static void +CreateNoNaNMask(itk::Image* itkValue, mitk::Image::Pointer mask, mitk::Image::Pointer& newMask) +{ + typedef itk::Image< TPixel, VImageDimension> LFloatImageType; + typedef itk::Image< unsigned short, VImageDimension> LMaskImageType; + typename LMaskImageType::Pointer itkMask = LMaskImageType::New(); + + mitk::CastToItkImage(mask, itkMask); + + typedef itk::ImageDuplicator< LMaskImageType > DuplicatorType; + typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); + duplicator->SetInputImage(itkMask); + duplicator->Update(); + + auto tmpMask = duplicator->GetOutput(); + + itk::ImageRegionIterator mask1Iter(itkMask, itkMask->GetLargestPossibleRegion()); + itk::ImageRegionIterator mask2Iter(tmpMask, tmpMask->GetLargestPossibleRegion()); + itk::ImageRegionIterator imageIter(itkValue, itkValue->GetLargestPossibleRegion()); + while (!mask1Iter.IsAtEnd()) + { + mask2Iter.Set(0); + if (mask1Iter.Value() > 0) + { + // Is not NaN + if (imageIter.Value() == imageIter.Value()) + { + mask2Iter.Set(1); + } + } + ++mask1Iter; + ++mask2Iter; + ++imageIter; + } + + newMask->InitializeByItk(tmpMask); + mitk::GrabItkImageMemory(tmpMask, newMask); +} + +template +static void +ResampleMask(itk::Image* itkMoving, mitk::Image::Pointer ref, mitk::Image::Pointer& newMask) +{ + typedef itk::Image< TPixel, VImageDimension> LMaskImageType; + typedef itk::NearestNeighborInterpolateImageFunction< LMaskImageType> NearestNeighborInterpolateImageFunctionType; + typedef itk::ResampleImageFilter ResampleFilterType; + + typename NearestNeighborInterpolateImageFunctionType::Pointer nn_interpolator = NearestNeighborInterpolateImageFunctionType::New(); + typename LMaskImageType::Pointer itkRef = LMaskImageType::New(); + mitk::CastToItkImage(ref, itkRef); + + + typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); + resampler->SetInput(itkMoving); + resampler->SetReferenceImage(itkRef); + resampler->UseReferenceImageOn(); + resampler->SetInterpolator(nn_interpolator); + resampler->Update(); + + newMask->InitializeByItk(resampler->GetOutput()); + mitk::GrabItkImageMemory(resampler->GetOutput(), newMask); +} + +static void +ExtractSlicesFromImages(mitk::Image::Pointer image, mitk::Image::Pointer mask, + mitk::Image::Pointer maskNoNaN, mitk::Image::Pointer morphMask, + int direction, + std::vector &imageVector, + std::vector &maskVector, + std::vector &maskNoNaNVector, + std::vector &morphMaskVector) +{ + typedef itk::Image< double, 2 > FloatImage2DType; + typedef itk::Image< unsigned short, 2 > MaskImage2DType; -static std::vector splitDouble(std::string str, char delimiter) { - std::vector internal; - std::stringstream ss(str); // Turn the string into a stream. - std::string tok; - double val; - while (std::getline(ss, tok, delimiter)) { - std::stringstream s2(tok); - s2 >> val; - internal.push_back(val); + FloatImageType::Pointer itkFloat = FloatImageType::New(); + MaskImageType::Pointer itkMask = MaskImageType::New(); + MaskImageType::Pointer itkMaskNoNaN = MaskImageType::New(); + MaskImageType::Pointer itkMorphMask = MaskImageType::New(); + mitk::CastToItkImage(mask, itkMask); + mitk::CastToItkImage(maskNoNaN, itkMaskNoNaN); + mitk::CastToItkImage(image, itkFloat); + mitk::CastToItkImage(morphMask, itkMorphMask); + + int idxA, idxB, idxC; + switch (direction) + { + case 0: + idxA = 1; idxB = 2; idxC = 0; + break; + case 1: + idxA = 0; idxB = 2; idxC = 1; + break; + case 2: + idxA = 0; idxB = 1; idxC = 2; + break; + default: + idxA = 1; idxB = 2; idxC = 0; + break; + } + + auto imageSize = image->GetLargestPossibleRegion().GetSize(); + FloatImageType::IndexType index3D; + FloatImage2DType::IndexType index2D; + FloatImage2DType::SpacingType spacing2D; + spacing2D[0] = itkFloat->GetSpacing()[idxA]; + spacing2D[1] = itkFloat->GetSpacing()[idxB]; + + for (unsigned int i = 0; i < imageSize[idxC]; ++i) + { + FloatImage2DType::RegionType region; + FloatImage2DType::IndexType start; + FloatImage2DType::SizeType size; + start[0] = 0; start[1] = 0; + size[0] = imageSize[idxA]; + size[1] = imageSize[idxB]; + region.SetIndex(start); + region.SetSize(size); + + FloatImage2DType::Pointer image2D = FloatImage2DType::New(); + image2D->SetRegions(region); + image2D->Allocate(); + + MaskImage2DType::Pointer mask2D = MaskImage2DType::New(); + mask2D->SetRegions(region); + mask2D->Allocate(); + + MaskImage2DType::Pointer masnNoNaN2D = MaskImage2DType::New(); + masnNoNaN2D->SetRegions(region); + masnNoNaN2D->Allocate(); + + MaskImage2DType::Pointer morph2D = MaskImage2DType::New(); + morph2D->SetRegions(region); + morph2D->Allocate(); + + + unsigned long voxelsInMask = 0; + + for (unsigned int a = 0; a < imageSize[idxA]; ++a) + { + for (unsigned int b = 0; b < imageSize[idxB]; ++b) + { + index3D[idxA] = a; + index3D[idxB] = b; + index3D[idxC] = i; + index2D[0] = a; + index2D[1] = b; + image2D->SetPixel(index2D, itkFloat->GetPixel(index3D)); + mask2D->SetPixel(index2D, itkMask->GetPixel(index3D)); + masnNoNaN2D->SetPixel(index2D, itkMaskNoNaN->GetPixel(index3D)); + morph2D->SetPixel(index2D, itkMorphMask->GetPixel(index3D)); + voxelsInMask += (itkMask->GetPixel(index3D) > 0) ? 1 : 0; + + } + } + + image2D->SetSpacing(spacing2D); + mask2D->SetSpacing(spacing2D); + masnNoNaN2D->SetSpacing(spacing2D); + morph2D->SetSpacing(spacing2D); + + mitk::Image::Pointer tmpFloatImage = mitk::Image::New(); + tmpFloatImage->InitializeByItk(image2D.GetPointer()); + mitk::GrabItkImageMemory(image2D, tmpFloatImage); + + mitk::Image::Pointer tmpMaskImage = mitk::Image::New(); + tmpMaskImage->InitializeByItk(mask2D.GetPointer()); + mitk::GrabItkImageMemory(mask2D, tmpMaskImage); + + mitk::Image::Pointer tmpMaskNoNaNImage = mitk::Image::New(); + tmpMaskNoNaNImage->InitializeByItk(masnNoNaN2D.GetPointer()); + mitk::GrabItkImageMemory(masnNoNaN2D, tmpMaskNoNaNImage); + + mitk::Image::Pointer tmpMorphMaskImage = mitk::Image::New(); + tmpMorphMaskImage->InitializeByItk(morph2D.GetPointer()); + mitk::GrabItkImageMemory(morph2D, tmpMorphMaskImage); + + if (voxelsInMask > 0) + { + imageVector.push_back(tmpFloatImage); + maskVector.push_back(tmpMaskImage); + maskNoNaNVector.push_back(tmpMaskNoNaNImage); + morphMaskVector.push_back(tmpMorphMaskImage); + } } +} + +static +void SaveSliceOrImageAsPNG(mitk::Image::Pointer image, mitk::Image::Pointer mask, std::string path, int index) +{ + // Create a Standalone Datastorage for the single purpose of saving screenshots.. + mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); + QmitkRenderWindow renderWindow; + renderWindow.GetRenderer()->SetDataStorage(ds); + + auto nodeI = mitk::DataNode::New(); + nodeI->SetData(image); + auto nodeM = mitk::DataNode::New(); + nodeM->SetData(mask); + ds->Add(nodeI); + ds->Add(nodeM); + + mitk::TimeGeometry::Pointer geo = ds->ComputeBoundingGeometry3D(ds->GetAll()); + mitk::RenderingManager::GetInstance()->InitializeViews(geo); + + mitk::SliceNavigationController::Pointer sliceNaviController = renderWindow.GetSliceNavigationController(); + unsigned int numberOfSteps = 1; + if (sliceNaviController) + { + numberOfSteps = sliceNaviController->GetSlice()->GetSteps(); + sliceNaviController->GetSlice()->SetPos(0); + } + + renderWindow.show(); + renderWindow.resize(256, 256); - return internal; + for (unsigned int currentStep = 0; currentStep < numberOfSteps; ++currentStep) + { + if (sliceNaviController) + { + sliceNaviController->GetSlice()->SetPos(currentStep); + } + + renderWindow.GetRenderer()->PrepareRender(); + + vtkRenderWindow* renderWindow2 = renderWindow.GetVtkRenderWindow(); + mitk::BaseRenderer* baserenderer = mitk::BaseRenderer::GetInstance(renderWindow2); + auto vtkRender = baserenderer->GetVtkRenderer(); + vtkRender->GetRenderWindow()->WaitForCompletion(); + + vtkRenderLargeImage* magnifier = vtkRenderLargeImage::New(); + magnifier->SetInput(vtkRender); + magnifier->SetMagnification(3.0); + + std::stringstream ss; + ss << path << "_Idx-" << index << "_Step-"<> tmpImageName; + auto fileWriter = vtkPNGWriter::New(); + fileWriter->SetInputConnection(magnifier->GetOutputPort()); + fileWriter->SetFileName(tmpImageName.c_str()); + fileWriter->Write(); + fileWriter->Delete(); + } } int main(int argc, char* argv[]) { + // Commented : Updated to a common interface, include, if possible, mask is type unsigned short, uses Quantification, Comments + // Name follows standard scheme with Class Name::Feature Name + // Commented 2: Updated to use automatic inclusion of list of parameters if required. + mitk::GIFImageDescriptionFeatures::Pointer ipCalculator = mitk::GIFImageDescriptionFeatures::New(); // Commented 2 + mitk::GIFFirstOrderStatistics::Pointer firstOrderCalculator = mitk::GIFFirstOrderStatistics::New(); //Commented 2 + mitk::GIFFirstOrderHistogramStatistics::Pointer firstOrderHistoCalculator = mitk::GIFFirstOrderHistogramStatistics::New(); // Commented 2 + mitk::GIFVolumetricStatistics::Pointer volCalculator = mitk::GIFVolumetricStatistics::New(); // Commented 2 + mitk::GIFVolumetricDensityStatistics::Pointer voldenCalculator = mitk::GIFVolumetricDensityStatistics::New(); // Commented 2 + mitk::GIFCooccurenceMatrix::Pointer coocCalculator = mitk::GIFCooccurenceMatrix::New(); // Commented 2 + mitk::GIFCooccurenceMatrix2::Pointer cooc2Calculator = mitk::GIFCooccurenceMatrix2::New(); //Commented 2 + mitk::GIFNeighbouringGreyLevelDependenceFeature::Pointer ngldCalculator = mitk::GIFNeighbouringGreyLevelDependenceFeature::New(); //Commented 2 + mitk::GIFGreyLevelRunLength::Pointer rlCalculator = mitk::GIFGreyLevelRunLength::New(); // Commented 2 + mitk::GIFGreyLevelSizeZone::Pointer glszCalculator = mitk::GIFGreyLevelSizeZone::New(); // Commented 2 + mitk::GIFGreyLevelDistanceZone::Pointer gldzCalculator = mitk::GIFGreyLevelDistanceZone::New(); //Commented 2 + mitk::GIFLocalIntensity::Pointer lociCalculator = mitk::GIFLocalIntensity::New(); //Commented 2 + mitk::GIFIntensityVolumeHistogramFeatures::Pointer ivohCalculator = mitk::GIFIntensityVolumeHistogramFeatures::New(); // Commented 2 + mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::Pointer ngtdCalculator = mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::New(); //Commented 2 + mitk::GIFCurvatureStatistic::Pointer curvCalculator = mitk::GIFCurvatureStatistic::New(); //Commented 2 + + std::vector features; + features.push_back(volCalculator.GetPointer()); + features.push_back(voldenCalculator.GetPointer()); + features.push_back(curvCalculator.GetPointer()); + features.push_back(firstOrderCalculator.GetPointer()); + features.push_back(firstOrderHistoCalculator.GetPointer()); + features.push_back(ivohCalculator.GetPointer()); + features.push_back(lociCalculator.GetPointer()); + features.push_back(coocCalculator.GetPointer()); + features.push_back(cooc2Calculator.GetPointer()); + features.push_back(ngldCalculator.GetPointer()); + features.push_back(rlCalculator.GetPointer()); + features.push_back(glszCalculator.GetPointer()); + features.push_back(gldzCalculator.GetPointer()); + features.push_back(ipCalculator.GetPointer()); + features.push_back(ngtdCalculator.GetPointer()); + mitkCommandLineParser parser; parser.setArgumentPrefix("--", "-"); - // required params - parser.addArgument("image", "i", mitkCommandLineParser::InputImage, "Input Image", "Path to the input VTK polydata", us::Any(), false); - parser.addArgument("mask", "m", mitkCommandLineParser::InputImage, "Input Mask", "Mask Image that specifies the area over for the statistic, (Values = 1)", us::Any(), false); - parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output text file", "Target file. The output statistic is appended to this file.", us::Any(), false); - - parser.addArgument("cooccurence","cooc",mitkCommandLineParser::String, "Use Co-occurence matrix", "calculates Co-occurence based features",us::Any()); - parser.addArgument("volume","vol",mitkCommandLineParser::String, "Use Volume-Statistic", "calculates volume based features",us::Any()); - parser.addArgument("run-length","rl",mitkCommandLineParser::String, "Use Co-occurence matrix", "calculates Co-occurence based features",us::Any()); - parser.addArgument("first-order","fo",mitkCommandLineParser::String, "Use First Order Features", "calculates First order based features",us::Any()); - parser.addArgument("header","head",mitkCommandLineParser::String,"Add Header (Labels) to output","",us::Any()); + mitk::cl::GlobalImageFeaturesParameter param; + param.AddParameter(parser); + + parser.addArgument("--","-", mitkCommandLineParser::String, "---", "---", us::Any(),true); + for (auto cFeature : features) + { + cFeature->AddArguments(parser); + } + + parser.addArgument("--", "-", mitkCommandLineParser::String, "---", "---", us::Any(), true); parser.addArgument("description","d",mitkCommandLineParser::String,"Text","Description that is added to the output",us::Any()); - parser.addArgument("same-space", "sp", mitkCommandLineParser::String, "Bool", "Set the spacing of all images to equal. Otherwise an error will be thrown. ", us::Any()); parser.addArgument("direction", "dir", mitkCommandLineParser::String, "Int", "Allows to specify the direction for Cooc and RL. 0: All directions, 1: Only single direction (Test purpose), 2,3,4... Without dimension 0,1,2... ", us::Any()); + parser.addArgument("slice-wise", "slice", mitkCommandLineParser::String, "Int", "Allows to specify if the image is processed slice-wise (number giving direction) ", us::Any()); + parser.addArgument("output-mode", "omode", mitkCommandLineParser::Int, "Int", "Defines if the results of an image / slice are written in a single row (0 , default) or column (1)."); // Miniapp Infos parser.setCategory("Classification Tools"); parser.setTitle("Global Image Feature calculator"); parser.setDescription("Calculates different global statistics for a given segmentation / image combination"); parser.setContributor("MBI"); std::map parsedArgs = parser.parseArguments(argc, argv); + param.ParseParameter(parsedArgs); if (parsedArgs.size()==0) { return EXIT_FAILURE; } if ( parsedArgs.count("help") || parsedArgs.count("h")) { return EXIT_SUCCESS; } - MITK_INFO << "Version: "<< 1.3; + //bool savePNGofSlices = true; + //std::string folderForPNGOfSlices = "E:\\tmp\\bonekamp\\fig\\"; + + std::string version = "Version: 1.22"; + MITK_INFO << version; + + std::ofstream log; + if (param.useLogfile) + { + log.open(param.logfilePath, std::ios::app); + log << version; + log << "Image: " << param.imagePath; + log << "Mask: " << param.maskPath; + } + + + if (param.useDecimalPoint) + { + std::cout.imbue(std::locale(std::cout.getloc(), new punct_facet(param.decimalPoint))); + } + + mitk::Image::Pointer image; + mitk::Image::Pointer mask; + + mitk::Image::Pointer tmpImage = mitk::IOUtil::LoadImage(param.imagePath); + mitk::Image::Pointer tmpMask = mitk::IOUtil::LoadImage(param.maskPath); + image = tmpImage; + mask = tmpMask; + + mitk::Image::Pointer morphMask = mask; + if (param.useMorphMask) + { + morphMask = mitk::IOUtil::LoadImage(param.morphPath); + } + + log << " Check for Dimensions -"; + if ((image->GetDimension() != mask->GetDimension())) + { + MITK_INFO << "Dimension of image does not match. "; + MITK_INFO << "Correct one image, may affect the result"; + if (image->GetDimension() == 2) + { + mitk::Convert2Dto3DImageFilter::Pointer multiFilter2 = mitk::Convert2Dto3DImageFilter::New(); + multiFilter2->SetInput(tmpImage); + multiFilter2->Update(); + image = multiFilter2->GetOutput(); + } + if (mask->GetDimension() == 2) + { + mitk::Convert2Dto3DImageFilter::Pointer multiFilter3 = mitk::Convert2Dto3DImageFilter::New(); + multiFilter3->SetInput(tmpMask); + multiFilter3->Update(); + mask = multiFilter3->GetOutput(); + } + } - mitk::Image::Pointer image = dynamic_cast(mitk::IOUtil::Load(parsedArgs["image"].ToString())[0].GetPointer()); - mitk::Image::Pointer mask = dynamic_cast(mitk::IOUtil::Load(parsedArgs["mask"].ToString())[0].GetPointer()); + int writeDirection = 0; + if (parsedArgs.count("output-mode")) + { + writeDirection = us::any_cast(parsedArgs["output-mode"]); + } - bool fixDifferentSpaces = parsedArgs.count("same-space"); + log << " Check for Resolution -"; + if (param.resampleToFixIsotropic) + { + mitk::Image::Pointer newImage = mitk::Image::New(); + AccessByItk_2(image, ResampleImage, param.resampleResolution, newImage); + image = newImage; + } if ( ! mitk::Equal(mask->GetGeometry(0)->GetOrigin(), image->GetGeometry(0)->GetOrigin())) { MITK_INFO << "Not equal Origins"; - if (fixDifferentSpaces) + if (param.ensureSameSpace) { + MITK_INFO << "Warning!"; + MITK_INFO << "The origin of the input image and the mask do not match. They are"; + MITK_INFO << "now corrected. Please check to make sure that the images still match"; image->GetGeometry(0)->SetOrigin(mask->GetGeometry(0)->GetOrigin()); } else { return -1; } } + + log << " Resample if required -"; + if (param.resampleMask) + { + mitk::Image::Pointer newMaskImage = mitk::Image::New(); + AccessByItk_2(mask, ResampleMask, image, newMaskImage); + mask = newMaskImage; + } + + log << " Check for Equality -"; if ( ! mitk::Equal(mask->GetGeometry(0)->GetSpacing(), image->GetGeometry(0)->GetSpacing())) { MITK_INFO << "Not equal Sapcings"; - if (fixDifferentSpaces) + if (param.ensureSameSpace) { + MITK_INFO << "Warning!"; + MITK_INFO << "The spacing of the mask was set to match the spacing of the input image."; + MITK_INFO << "This might cause unintended spacing of the mask image"; image->GetGeometry(0)->SetSpacing(mask->GetGeometry(0)->GetSpacing()); } else { + MITK_INFO << "The spacing of the mask and the input images is not equal."; + MITK_INFO << "Terminating the programm. You may use the '-fi' option"; return -1; } } int direction = 0; if (parsedArgs.count("direction")) { - direction = splitDouble(parsedArgs["direction"].ToString(), ';')[0]; + direction = mitk::cl::splitDouble(parsedArgs["direction"].ToString(), ';')[0]; } - mitk::AbstractGlobalImageFeature::FeatureListType stats; - //////////////////////////////////////////////////////////////// - // CAlculate First Order Features - //////////////////////////////////////////////////////////////// - if (parsedArgs.count("first-order")) - { - MITK_INFO << "Start calculating first order statistics...."; - mitk::GIFFirstOrderStatistics::Pointer firstOrderCalculator = mitk::GIFFirstOrderStatistics::New(); - auto localResults = firstOrderCalculator->CalculateFeatures(image, mask); - stats.insert(stats.end(), localResults.begin(), localResults.end()); - MITK_INFO << "Finished calculating first order statistics...."; - } + MITK_INFO << "Start creating Mask without NaN"; - //////////////////////////////////////////////////////////////// - // CAlculate Volume based Features - //////////////////////////////////////////////////////////////// - if (parsedArgs.count("volume")) + mitk::Image::Pointer maskNoNaN = mitk::Image::New(); + AccessByItk_2(image, CreateNoNaNMask, mask, maskNoNaN); + //CreateNoNaNMask(mask, image, maskNoNaN); + + + bool sliceWise = false; + int sliceDirection = 0; + unsigned int currentSlice = 0; + bool imageToProcess = true; + + std::vector floatVector; + std::vector maskVector; + std::vector maskNoNaNVector; + std::vector morphMaskVector; + + if ((parsedArgs.count("slice-wise")) && image->GetDimension() > 2) { - MITK_INFO << "Start calculating volumetric ...."; - mitk::GIFVolumetricStatistics::Pointer volCalculator = mitk::GIFVolumetricStatistics::New(); - auto localResults = volCalculator->CalculateFeatures(image, mask); - stats.insert(stats.end(), localResults.begin(), localResults.end()); - MITK_INFO << "Finished calculating volumetric...."; + MITK_INFO << "Enabled slice-wise"; + sliceWise = true; + sliceDirection = mitk::cl::splitDouble(parsedArgs["slice-wise"].ToString(), ';')[0]; + MITK_INFO << sliceDirection; + ExtractSlicesFromImages(image, mask, maskNoNaN, morphMask, sliceDirection, floatVector, maskVector, maskNoNaNVector, morphMaskVector); + MITK_INFO << "Slice"; } - //////////////////////////////////////////////////////////////// - // CAlculate Co-occurence Features - //////////////////////////////////////////////////////////////// - if (parsedArgs.count("cooccurence")) + log << " Configure features -"; + for (auto cFeature : features) { - auto ranges = splitDouble(parsedArgs["cooccurence"].ToString(),';'); - - for (std::size_t i = 0; i < ranges.size(); ++i) + if (param.defineGlobalMinimumIntensity) { - MITK_INFO << "Start calculating coocurence with range " << ranges[i] << "...."; - mitk::GIFCooccurenceMatrix::Pointer coocCalculator = mitk::GIFCooccurenceMatrix::New(); - coocCalculator->SetRange(ranges[i]); - coocCalculator->SetDirection(direction); - auto localResults = coocCalculator->CalculateFeatures(image, mask); - stats.insert(stats.end(), localResults.begin(), localResults.end()); - MITK_INFO << "Finished calculating coocurence with range " << ranges[i] << "...."; + cFeature->SetMinimumIntensity(param.globalMinimumIntensity); + cFeature->SetUseMinimumIntensity(true); } + if (param.defineGlobalMaximumIntensity) + { + cFeature->SetMaximumIntensity(param.globalMaximumIntensity); + cFeature->SetUseMaximumIntensity(true); + } + if (param.defineGlobalNumberOfBins) + { + cFeature->SetBins(param.globalNumberOfBins); + MITK_INFO << param.globalNumberOfBins; + } + cFeature->SetParameter(parsedArgs); + cFeature->SetDirection(direction); + cFeature->SetEncodeParameters(param.encodeParameter); } - //////////////////////////////////////////////////////////////// - // CAlculate Run-Length Features - //////////////////////////////////////////////////////////////// - if (parsedArgs.count("run-length")) - { - auto ranges = splitDouble(parsedArgs["run-length"].ToString(),';'); + bool addDescription = parsedArgs.count("description"); + mitk::cl::FeatureResultWritter writer(param.outputPath, writeDirection); - for (std::size_t i = 0; i < ranges.size(); ++i) - { - MITK_INFO << "Start calculating run-length with number of bins " << ranges[i] << "...."; - mitk::GIFGrayLevelRunLength::Pointer calculator = mitk::GIFGrayLevelRunLength::New(); - calculator->SetRange(ranges[i]); + if (param.useDecimalPoint) + { + writer.SetDecimalPoint(param.decimalPoint); + } - auto localResults = calculator->CalculateFeatures(image, mask); - stats.insert(stats.end(), localResults.begin(), localResults.end()); - MITK_INFO << "Finished calculating run-length with number of bins " << ranges[i] << "...."; - } + std::string description = ""; + if (addDescription) + { + description = parsedArgs["description"].ToString(); } - for (std::size_t i = 0; i < stats.size(); ++i) + + mitk::Image::Pointer cImage = image; + mitk::Image::Pointer cMask = mask; + mitk::Image::Pointer cMaskNoNaN = maskNoNaN; + mitk::Image::Pointer cMorphMask = morphMask; + + if (param.useHeader) { - std::cout << stats[i].first << " - " << stats[i].second < allStats; + + log << " Begin Processing -"; + while (imageToProcess) { - if ( parsedArgs.count("description") ) + if (sliceWise) + { + cImage = floatVector[currentSlice]; + cMask = maskVector[currentSlice]; + cMaskNoNaN = maskNoNaNVector[currentSlice]; + cMorphMask = morphMaskVector[currentSlice]; + imageToProcess = (floatVector.size()-1 > (currentSlice)) ? true : false ; + } + else + { + imageToProcess = false; + } + + if (param.writePNGScreenshots) + { + SaveSliceOrImageAsPNG(cImage, cMask, param.pngScreenshotsPath, currentSlice); + } + if (param.writeAnalysisImage) + { + mitk::IOUtil::Save(cImage, param.anaylsisImagePath); + } + if (param.writeAnalysisMask) { - output << "Description" << ";"; + mitk::IOUtil::Save(cMask, param.analysisMaskPath); } + + mitk::AbstractGlobalImageFeature::FeatureListType stats; + + for (auto cFeature : features) + { + log << " Calculating " << cFeature->GetFeatureClassName() << " -"; + cFeature->SetMorphMask(cMorphMask); + cFeature->CalculateFeaturesUsingParameters(cImage, cMask, cMaskNoNaN, stats); + } + for (std::size_t i = 0; i < stats.size(); ++i) { - output << stats[i].first << ";"; + std::cout << stats[i].first << " - " << stats[i].second << std::endl; } - output << std::endl; + + writer.AddHeader(description, currentSlice, stats, param.useHeader, addDescription); + if (true) + { + writer.AddSubjectInformation(MITK_REVISION); + writer.AddSubjectInformation(param.imageFolder); + writer.AddSubjectInformation(param.imageName); + writer.AddSubjectInformation(param.maskName); + } + writer.AddResult(description, currentSlice, stats, param.useHeader, addDescription); + + allStats.push_back(stats); + ++currentSlice; } - if ( parsedArgs.count("description") ) + + log << " Process Slicewise -"; + if (sliceWise) { - output << parsedArgs["description"].ToString() << ";"; + mitk::AbstractGlobalImageFeature::FeatureListType statMean, statStd; + for (std::size_t i = 0; i < allStats[0].size(); ++i) + { + auto cElement1 = allStats[0][i]; + cElement1.first = "SliceWise Mean " + cElement1.first; + cElement1.second = 0.0; + auto cElement2 = allStats[0][i]; + cElement2.first = "SliceWise Var. " + cElement2.first; + cElement2.second = 0.0; + statMean.push_back(cElement1); + statStd.push_back(cElement2); + } + + for (auto cStat : allStats) + { + for (std::size_t i = 0; i < cStat.size(); ++i) + { + statMean[i].second += cStat[i].second / (1.0*allStats.size()); + } + } + + for (auto cStat : allStats) + { + for (std::size_t i = 0; i < cStat.size(); ++i) + { + statStd[i].second += (cStat[i].second - statMean[i].second)*(cStat[i].second - statMean[i].second) / (1.0*allStats.size()); + } + } + + for (std::size_t i = 0; i < statMean.size(); ++i) + { + std::cout << statMean[i].first << " - " << statMean[i].second << std::endl; + std::cout << statStd[i].first << " - " << statStd[i].second << std::endl; + } + if (true) + { + writer.AddSubjectInformation(MITK_REVISION); + writer.AddSubjectInformation(param.imageFolder); + writer.AddSubjectInformation(param.imageName); + writer.AddSubjectInformation(param.maskName + " - Mean"); + } + writer.AddResult(description, currentSlice, statMean, param.useHeader, addDescription); + if (true) + { + writer.AddSubjectInformation(MITK_REVISION); + writer.AddSubjectInformation(param.imageFolder); + writer.AddSubjectInformation(param.imageName); + writer.AddSubjectInformation(param.maskName + " - Var."); + } + writer.AddResult(description, currentSlice, statStd, param.useHeader, addDescription); } - for (std::size_t i = 0; i < stats.size(); ++i) + + if (param.useLogfile) { - output << stats[i].second << ";"; + log << "Finished calculation" << std::endl; + log.close(); } - output << std::endl; - output.close(); - return 0; } #endif diff --git a/Modules/Classification/CLMiniApps/CLImageTypeConverter.cpp b/Modules/Classification/CLMiniApps/CLImageTypeConverter.cpp new file mode 100644 index 0000000000..41ab9734a5 --- /dev/null +++ b/Modules/Classification/CLMiniApps/CLImageTypeConverter.cpp @@ -0,0 +1,108 @@ +/*=================================================================== + +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 "mitkCommandLineParser.h" +#include "mitkIOUtil.h" +#include +#include + +#define CONVERT_IMAGE(TYPE, DIM) itk::Image::Pointer itkImage = itk::Image::New(); \ + MITK_INFO << "Data Type for Conversion: "<< typeid(TYPE).name(); \ + mitk::CastToItkImage(image, itkImage); \ + mitk::CastToMitkImage(itkImage, outputImage) + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + + parser.setTitle("Image Type Converter"); + parser.setCategory("Preprocessing Tools"); + parser.setDescription(""); + parser.setContributor("MBI"); + + parser.setArgumentPrefix("--","-"); + // Add command line argument names + parser.addArgument("help", "h",mitkCommandLineParser::Bool, "Help:", "Show this help text"); + parser.addArgument("input", "i", mitkCommandLineParser::InputDirectory, "Input file:", "Input file",us::Any(),false); + parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output file:", "Output file", us::Any(), false); + parser.addArgument("type", "t", mitkCommandLineParser::OutputFile, "Type definition:", "Define Scalar data type: int, uint, short, ushort, char, uchar, float, double", us::Any(), false); + + std::map parsedArgs = parser.parseArguments(argc, argv); + + if (parsedArgs.size()==0) + return EXIT_FAILURE; + + // Show a help message + if ( parsedArgs.count("help") || parsedArgs.count("h")) + { + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + + std::string inputName = us::any_cast(parsedArgs["input"]); + std::string outputName = us::any_cast(parsedArgs["output"]); + std::string type = us::any_cast(parsedArgs["type"]); + + mitk::Image::Pointer image = mitk::IOUtil::LoadImage(inputName); + mitk::Image::Pointer outputImage = mitk::Image::New(); + + if (type.compare("int") == 0) + { + CONVERT_IMAGE(int, 3); + } + else if (type.compare("uint") == 0) + { + CONVERT_IMAGE(unsigned int, 3); + } + else if (type.compare("char") == 0) + { + CONVERT_IMAGE(char, 3); + } + else if (type.compare("uchar") == 0) + { + CONVERT_IMAGE(unsigned char, 3); + } + else if (type.compare("short") == 0) + { + CONVERT_IMAGE(short, 3); + } + else if (type.compare("ushort") == 0) + { + CONVERT_IMAGE(unsigned short, 3); + } + else if (type.compare("float") == 0) + { + CONVERT_IMAGE(float, 3); + } + else if (type.compare("double") == 0) + { + CONVERT_IMAGE(double, 3); + } + else if (type.compare("none") == 0) + { + MITK_INFO << " No conversion performed"; + outputImage = image; + } + else + { + CONVERT_IMAGE(double, 3); + } + + + mitk::IOUtil::Save(outputImage, outputName); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/Modules/Classification/CLMiniApps/CLLungSegmentation.cpp b/Modules/Classification/CLMiniApps/CLLungSegmentation.cpp new file mode 100644 index 0000000000..3350c8bd4d --- /dev/null +++ b/Modules/Classification/CLMiniApps/CLLungSegmentation.cpp @@ -0,0 +1,177 @@ +/*=================================================================== + +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 "mitkDicomSeriesReader.h" +#include "mitkProperties.h" + +#include "mitkCommandLineParser.h" +#include "mitkIOUtil.h" + +#include +#include "mitkLabelSetImage.h" + +#include "mitkImageCast.h" +#include "mitkImageTimeSelector.h" +#include "mitkITKImageImport.h" +#include "mitkImageAccessByItk.h" + +#include + +#include + +template +void StartRegionGrowing(itk::Image* itkImage, mitk::Image::Pointer &result) +{ + typedef itk::Image InputImageType; + typedef typename InputImageType::IndexType IndexType; + typedef itk::ConnectedThresholdImageFilter RegionGrowingFilterType; + typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); + + // convert world coordinates to image indices + IndexType startIndex; + IndexType seedIndex; + IndexType bestSeedIndex; + startIndex[0] = itkImage->GetLargestPossibleRegion().GetSize()[0]/2; + startIndex[1] = itkImage->GetLargestPossibleRegion().GetSize()[1]/2; + startIndex[2] = itkImage->GetLargestPossibleRegion().GetSize()[2]/2; + auto region = itkImage->GetLargestPossibleRegion(); + auto spacing = itkImage->GetSpacing(); + spacing[0] = itkImage->GetSpacing()[0]; + spacing[1] = itkImage->GetSpacing()[1]; + spacing[2] = itkImage->GetSpacing()[2]; + + int minimumDistance = 50 * 50 * (spacing[0] + spacing[1] + spacing[2]); + + for (int x = -50; x < 50; ++x) + { + for (int y = -50; y < 50; ++y) + { + for (int z = -20; z < 20; ++z) + { + seedIndex[0] = startIndex[0] + x; + seedIndex[1] = startIndex[1] + y; + seedIndex[2] = startIndex[2] + z; + if (region.IsInside(seedIndex)) + { + if (itkImage->GetPixel(seedIndex) > 0) + { + int newDistance = x*x*spacing[0] + y*y*spacing[1] + z*z*spacing[2]; + if (newDistance < minimumDistance) + { + bestSeedIndex = seedIndex; + minimumDistance = newDistance; + } + } + } + } + } + } + seedIndex = bestSeedIndex; + + MITK_INFO << "Seedpoint: " << seedIndex; + //perform region growing in desired segmented region + regionGrower->SetInput(itkImage); + regionGrower->AddSeed(seedIndex); + + regionGrower->SetLower(1); + regionGrower->SetUpper(255); + + try + { + regionGrower->Update(); + } + catch (const itk::ExceptionObject&) + { + return; // can't work + } + catch (...) + { + return; + } + + //Store result and preview + mitk::CastToMitkImage(regionGrower->GetOutput(), result); +} + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + + parser.setTitle("Dicom Loader"); + parser.setCategory("Preprocessing Tools"); + parser.setDescription(""); + parser.setContributor("MBI"); + + parser.setArgumentPrefix("--","-"); + // Add command line argument names + parser.addArgument("help", "h",mitkCommandLineParser::Bool, "Help:", "Show this help text"); + parser.addArgument("input", "i", mitkCommandLineParser::InputDirectory, "Input folder:", "Input folder", us::Any(), false); + parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output file:", "Output file",us::Any(),false); + + + std::map parsedArgs = parser.parseArguments(argc, argv); + + if (parsedArgs.size()==0) + return EXIT_FAILURE; + + // Show a help message + if ( parsedArgs.count("help") || parsedArgs.count("h")) + { + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + + std::string inputFile = us::any_cast(parsedArgs["input"]); + std::string outFileName = us::any_cast(parsedArgs["output"]); + + MITK_INFO << "Start Image Loading"; + + mitk::Image::Pointer image = mitk::IOUtil::LoadImage(inputFile); + + MITK_INFO << "Loaded Image"; + + mitk::OtsuSegmentationFilter::Pointer otsuFilter = mitk::OtsuSegmentationFilter::New(); + otsuFilter->SetNumberOfThresholds(2); + otsuFilter->SetValleyEmphasis(false); + otsuFilter->SetNumberOfBins(128); + otsuFilter->SetInput(image); + try + { + otsuFilter->Update(); + } + catch (...) + { + mitkThrow() << "itkOtsuFilter error (image dimension must be in {2, 3} and image must not be RGB)"; + } + + MITK_INFO << "Calculated Otsu"; + + mitk::LabelSetImage::Pointer resultImage = mitk::LabelSetImage::New(); + resultImage->InitializeByLabeledImage(otsuFilter->GetOutput()); + mitk::Image::Pointer rawMask = resultImage->CreateLabelMask(1); + mitk::Image::Pointer pickedMask; + + AccessByItk_1(rawMask, StartRegionGrowing, pickedMask); + + mitk::MorphologicalOperations::FillHoles(pickedMask); + mitk::MorphologicalOperations::Closing(pickedMask, 5, mitk::MorphologicalOperations::StructuralElementType::Ball); + mitk::MorphologicalOperations::FillHoles(pickedMask); + + + mitk::IOUtil::Save(pickedMask, outFileName); + + return EXIT_SUCCESS; +} diff --git a/Modules/Classification/CLMiniApps/CLMRNormalization.cpp b/Modules/Classification/CLMiniApps/CLMRNormalization.cpp index d5e59d8556..453523dbd7 100644 --- a/Modules/Classification/CLMiniApps/CLMRNormalization.cpp +++ b/Modules/Classification/CLMiniApps/CLMRNormalization.cpp @@ -1,133 +1,165 @@ /*=================================================================== 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 mitkCLPolyToNrrd_cpp #define mitkCLPolyToNrrd_cpp #include "time.h" #include #include #include #include "mitkCommandLineParser.h" #include "itkImageRegionIterator.h" // MITK #include #include #include #include #include // ITK #include #include typedef itk::Image< double, 3 > FloatImageType; typedef itk::Image< unsigned char, 3 > MaskImageType; int main(int argc, char* argv[]) { MITK_INFO << "Start"; mitkCommandLineParser parser; parser.setArgumentPrefix("--", "-"); // required params parser.addArgument("image", "i", mitkCommandLineParser::InputImage, "Input Image", "Path to the input VTK polydata", us::Any(), false); parser.addArgument("mode", "mode", mitkCommandLineParser::InputImage, "Normalisation mode", "1,2,3: Single Area normalization to Mean, Median, Mode, 4,5,6: Mean, Median, Mode of two regions. ", us::Any(), false); parser.addArgument("mask0", "m0", mitkCommandLineParser::InputImage, "Input Mask", "The median of the area covered by this mask will be set to 0", us::Any(), false); parser.addArgument("mask1", "m1", mitkCommandLineParser::InputImage, "Input Mask", "The median of the area covered by this mask will be set to 1", us::Any(), true); parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output Image", "Target file. The output statistic is appended to this file.", us::Any(), false); + parser.addArgument("ignore-outlier", "outlier", mitkCommandLineParser::Bool, "Ignore Outlier", "Ignores the highest and lowest 2% during calculation. Only on single mask normalization.", us::Any(), true); + parser.addArgument("value", "v", mitkCommandLineParser::Float, "Target Value", "Target value, the target value (for example median) is set to this value.", us::Any(), true); + parser.addArgument("width", "w", mitkCommandLineParser::Float, "Target Width", "Ignores the highest and lowest 2% during calculation. Only on single mask normalization.", us::Any(), true); + parser.addArgument("float", "float", mitkCommandLineParser::Bool, "Target Width", "Ignores the highest and lowest 2% during calculation. Only on single mask normalization.", us::Any(), true); // Miniapp Infos parser.setCategory("Classification Tools"); parser.setTitle("MR Normalization Tool"); parser.setDescription("Normalizes a MR image. Sets the Median of the tissue covered by mask 0 to 0 and the median of the area covered by mask 1 to 1."); parser.setContributor("MBI"); std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) { return EXIT_FAILURE; } if ( parsedArgs.count("help") || parsedArgs.count("h")) { return EXIT_SUCCESS; } + bool ignore_outlier = false; + if (parsedArgs.count("ignore-outlier")) + { + ignore_outlier = us::any_cast(parsedArgs["ignore-outlier"]); + } + MITK_INFO << "Mode access"; - int mode = 5;//us::any_cast(parsedArgs["mode"]); + int mode =std::stoi(us::any_cast(parsedArgs["mode"])); + MITK_INFO << "Mode: " << mode; MITK_INFO << "Read images"; mitk::Image::Pointer mask1; mitk::Image::Pointer image = dynamic_cast(mitk::IOUtil::Load(parsedArgs["image"].ToString())[0].GetPointer()); + + if (parsedArgs.count("float")) + { + typedef itk::Image ImageType; + ImageType::Pointer img = ImageType::New(); + mitk::CastToItkImage(image, img); + mitk::CastToMitkImage(img, image); + } + mitk::Image::Pointer mask0 = dynamic_cast(mitk::IOUtil::Load(parsedArgs["mask0"].ToString())[0].GetPointer()); if (mode > 3) { mask1 = dynamic_cast(mitk::IOUtil::Load(parsedArgs["mask1"].ToString())[0].GetPointer()); } mitk::MRNormLinearStatisticBasedFilter::Pointer oneRegion = mitk::MRNormLinearStatisticBasedFilter::New(); mitk::MRNormTwoRegionsBasedFilter::Pointer twoRegion = mitk::MRNormTwoRegionsBasedFilter::New(); mitk::Image::Pointer output; - //oneRegion->SetInput(image); + oneRegion->SetInput(image); + oneRegion->SetMask(mask0); + oneRegion->SetIgnoreOutlier(ignore_outlier); twoRegion->SetInput(image); - //oneRegion->SetMask(mask0); twoRegion->SetMask1(mask0); twoRegion->SetMask2(mask1); + if (parsedArgs.count("value")) + { + double target = us::any_cast(parsedArgs["value"]); + oneRegion->SetTargetValue(target); + } + if (parsedArgs.count("width")) + { + double width = us::any_cast(parsedArgs["width"]); + oneRegion->SetTargetValue(width); + } + switch (mode) { case 1: oneRegion->SetCenterMode(mitk::MRNormLinearStatisticBasedFilter::MEAN); oneRegion->Update(); output=oneRegion->GetOutput(); break; case 2: oneRegion->SetCenterMode(mitk::MRNormLinearStatisticBasedFilter::MEDIAN); oneRegion->Update(); output=oneRegion->GetOutput(); break; case 3: oneRegion->SetCenterMode(mitk::MRNormLinearStatisticBasedFilter::MODE); oneRegion->Update(); output=oneRegion->GetOutput(); break; case 4: twoRegion->SetArea1(mitk::MRNormTwoRegionsBasedFilter::MEAN); twoRegion->SetArea2(mitk::MRNormTwoRegionsBasedFilter::MEAN); twoRegion->Update(); output=twoRegion->GetOutput(); break; case 5: twoRegion->SetArea1(mitk::MRNormTwoRegionsBasedFilter::MEDIAN); twoRegion->SetArea2(mitk::MRNormTwoRegionsBasedFilter::MEDIAN); twoRegion->Update(); output=twoRegion->GetOutput(); break; case 6: twoRegion->SetArea1(mitk::MRNormTwoRegionsBasedFilter::MODE); twoRegion->SetArea2(mitk::MRNormTwoRegionsBasedFilter::MODE); twoRegion->Update(); output=twoRegion->GetOutput(); break; } mitk::IOUtil::Save(output, parsedArgs["output"].ToString()); return 0; } #endif \ No newline at end of file diff --git a/Modules/Classification/CLMiniApps/CLMatchPointReg.cpp b/Modules/Classification/CLMiniApps/CLMatchPointReg.cpp new file mode 100644 index 0000000000..8db9bae5fb --- /dev/null +++ b/Modules/Classification/CLMiniApps/CLMatchPointReg.cpp @@ -0,0 +1,169 @@ +/*=================================================================== + +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 "mitkDicomSeriesReader.h" +#include "mitkProperties.h" + +#include "mitkCommandLineParser.h" +#include "mitkIOUtil.h" + +#include "mitkPreferenceListReaderOptionsFunctor.h" + + +// MatchPoint +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +// Qt +#include +#include +#include +//#include +#include +#include +#include +#include + + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + + parser.setTitle("Dicom Loader"); + parser.setCategory("Preprocessing Tools"); + parser.setDescription(""); + parser.setContributor("MBI"); + + parser.setArgumentPrefix("--","-"); + // Add command line argument names + parser.addArgument("help", "h",mitkCommandLineParser::Bool, "Help:", "Show this help text"); + parser.addArgument("moving", "m", mitkCommandLineParser::InputDirectory, "Input folder:", "Input folder", us::Any(), false); + parser.addArgument("fixed", "f", mitkCommandLineParser::InputDirectory, "Input folder:", "Input folder", us::Any(), false); + parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output file:", "Output file", us::Any(), false); + parser.addArgument("reader", "r", mitkCommandLineParser::Int, "Reader ID", "Reader Name", us::Any(), false); + parser.addArgument("interpolation", "interp", mitkCommandLineParser::Int, "Reader ID", "Reader Name", us::Any(), false); + + std::map parsedArgs = parser.parseArguments(argc, argv); + + QFileInfo fi(argv[0]); + map::deployment::DLLDirectoryBrowser::Pointer browser = map::deployment::DLLDirectoryBrowser::New(); + browser->addDLLSearchLocation(QDir::homePath().toStdString()); + browser->addDLLSearchLocation(QDir::currentPath().toStdString()); + browser->addDLLSearchLocation(fi.canonicalPath().toStdString()); + browser->update(); + auto dllList = browser->getLibraryInfos(); + + int id = 0; + std::cout << std::endl << " --- Algorithm List --- " << std::endl; + for (auto info : dllList) + { + std::cout << "Algorithm ID " << id << ": " << info->getAlgorithmUID().getName() << std::endl; + ++id; + } + std::cout << std::endl << " --- Interpolation List --- " << std::endl; + std::cout << "Interpolation ID 0: Linear Interpolation " << std::endl; + std::cout << "Interpolation ID 1: Nearest Neighbour" << std::endl; + std::cout << "Interpolation ID 2: BSpline 3D" << std::endl << std::endl; + + mitk::ImageMappingInterpolator::Type interpolationMode = mitk::ImageMappingInterpolator::Linear; + + if (parsedArgs.size()==0) + return EXIT_FAILURE; + + // Show a help message + if ( parsedArgs.count("help") || parsedArgs.count("h")) + { + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + + + std::string movingFile = us::any_cast(parsedArgs["moving"]); + std::string fixedFile = us::any_cast(parsedArgs["fixed"]); + int selectedAlgorithm = us::any_cast(parsedArgs["reader"]); + std::string outputPath = us::any_cast(parsedArgs["output"]); + + if (parsedArgs.count("interpolation")) + { + switch (us::any_cast(parsedArgs["interpolation"])) + { + case 0: + interpolationMode = mitk::ImageMappingInterpolator::Linear; + break; + case 1: + interpolationMode = mitk::ImageMappingInterpolator::NearestNeighbor; + break; + case 2: + interpolationMode = mitk::ImageMappingInterpolator::BSpline_3; + break; + default: + interpolationMode = mitk::ImageMappingInterpolator::Linear; + } + } + + + mitk::Image::Pointer movingImage = dynamic_cast(mitk::IOUtil::Load(movingFile)[0].GetPointer()); + mitk::Image::Pointer fixedImage = dynamic_cast(mitk::IOUtil::Load(fixedFile)[0].GetPointer()); + + auto dllInfo = dllList[selectedAlgorithm]; + + if (!dllInfo) + { + MITK_ERROR << "No valid algorithm is selected. Cannot load algorithm. ABORTING."; + return -1; + } + + ::map::deployment::DLLHandle::Pointer tempDLLHandle = ::map::deployment::openDeploymentDLL( + dllInfo->getLibraryFilePath()); + ::map::algorithm::RegistrationAlgorithmBase::Pointer tempAlgorithm + = ::map::deployment::getRegistrationAlgorithm(tempDLLHandle); + MITK_INFO << "Well...."; + if (tempAlgorithm.IsNull()) + { + MITK_ERROR << "Error. Cannot load selected algorithm."; + return -2; + } + + mitk::MITKAlgorithmHelper helper(tempAlgorithm); + helper.SetData(movingImage, fixedImage); + auto registration = helper.GetRegistration(); + MITK_INFO << "Well...."; + + mitk::Image::Pointer spResultData= mitk::ImageMappingHelper::map(movingImage, + registration, + false, // Use all Pixels + 0.0, // Padding Value + fixedImage->GetGeometry()->Clone().GetPointer(), // Ref. Geometry + false, //!(this->m_allowUnregPixels), + 0, // Error Value + interpolationMode // Interpolator Type + ); + + MITK_INFO << "Well...."; + mitk::IOUtil::Save(spResultData, outputPath); + + return EXIT_SUCCESS; +} diff --git a/Modules/Classification/CLMiniApps/CLMultiForestPrediction.cpp b/Modules/Classification/CLMiniApps/CLMultiForestPrediction.cpp new file mode 100644 index 0000000000..bc42b2a075 --- /dev/null +++ b/Modules/Classification/CLMiniApps/CLMultiForestPrediction.cpp @@ -0,0 +1,251 @@ +/*=================================================================== + +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 mitkForest_cpp +#define mitkForest_cpp + +#include "time.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// ----------------------- Forest Handling ---------------------- +#include + + +int main(int argc, char* argv[]) +{ + MITK_INFO << "Starting MITK_Forest Mini-App"; + + ////////////////////////////////////////////////////////////////////////////// + // Read Console Input Parameter + ////////////////////////////////////////////////////////////////////////////// + ConfigFileReader allConfig(argv[1]); + + bool readFile = true; + std::stringstream ss; + for (int i = 0; i < argc; ++i ) + { + MITK_INFO << "-----"<< argv[i]<<"------"; + if (readFile) + { + if (argv[i][0] == '+') + { + readFile = false; + continue; + } else + { + try + { + allConfig.ReadFile(argv[i]); + } + catch (std::exception &e) + { + MITK_INFO << e.what(); + } + } + } + else + { + std::string input = argv[i]; + std::replace(input.begin(), input.end(),'_',' '); + ss << input << std::endl; + } + } + allConfig.ReadStream(ss); + + try + { + ////////////////////////////////////////////////////////////////////////////// + // General + ////////////////////////////////////////////////////////////////////////////// + int currentRun = allConfig.IntValue("General","Run",0); + //int doTraining = allConfig.IntValue("General","Do Training",1); + std::string forestPath = allConfig.Value("General","Forest Path"); + std::string trainingCollectionPath = allConfig.Value("General","Patient Collection"); + std::string testCollectionPath = allConfig.Value("General", "Patient Test Collection", trainingCollectionPath); + + ////////////////////////////////////////////////////////////////////////////// + // Read Default Classification + ////////////////////////////////////////////////////////////////////////////// + std::vector trainPatients = allConfig.Vector("Training Group",currentRun); + std::vector testPatients = allConfig.Vector("Test Group",currentRun); + std::vector modalities = allConfig.Vector("Modalities", 0); + std::vector outputFilter = allConfig.Vector("Output Filter", 0); + std::string trainMask = allConfig.Value("Data","Training Mask"); + std::string completeTrainMask = allConfig.Value("Data","Complete Training Mask"); + std::string testMask = allConfig.Value("Data","Test Mask"); + std::string resultMask = allConfig.Value("Data", "Result Mask"); + std::string resultProb = allConfig.Value("Data", "Result Propability"); + std::string outputFolder = allConfig.Value("General","Output Folder"); + + std::string writeDataFilePath = allConfig.Value("Forest","File to write data to"); + + ////////////////////////////////////////////////////////////////////////////// + // Read Data Forest Parameter + ////////////////////////////////////////////////////////////////////////////// + int testSingleDataset = allConfig.IntValue("Data", "Test Single Dataset",0); + std::string singleDatasetName = allConfig.Value("Data", "Single Dataset Name", "none"); + std::vector forestVector = allConfig.Vector("Forests", 0); + + ////////////////////////////////////////////////////////////////////////////// + // Read Statistic Parameter + ////////////////////////////////////////////////////////////////////////////// + std::string statisticFilePath = allConfig.Value("Evaluation", "Statistic output file"); + std::string statisticShortFilePath = allConfig.Value("Evaluation", "Statistic short output file"); + std::string statisticShortFileLabel = allConfig.Value("Evaluation", "Index for short file"); + std::string statisticGoldStandard = allConfig.Value("Evaluation", "Gold Standard Name","GTV"); + //bool statisticWithHeader = allConfig.IntValue("Evaluation", "Write header in short file",0); + std::vector labelGroupA = allConfig.Vector("LabelsA",0); + std::vector labelGroupB = allConfig.Vector("LabelsB",0); + + + std::ofstream timingFile; + timingFile.open((statisticFilePath + ".timing").c_str(), std::ios::app); + timingFile << statisticShortFileLabel << ";"; + std::time_t lastTimePoint; + time(&lastTimePoint); + + ////////////////////////////////////////////////////////////////////////////// + // Read Images + ////////////////////////////////////////////////////////////////////////////// + std::vector usedModalities; + for (std::size_t i = 0; i < modalities.size(); ++i) + { + usedModalities.push_back(modalities[i]); + } + usedModalities.push_back(trainMask); + usedModalities.push_back(completeTrainMask); + usedModalities.push_back(testMask); + usedModalities.push_back(statisticGoldStandard); + + // vtkSmartPointer colReader = vtkSmartPointer::New(); + mitk::CollectionReader* colReader = new mitk::CollectionReader(); + colReader->AddDataElementIds(trainPatients); + colReader->SetDataItemNames(usedModalities); + + if (testSingleDataset > 0) + { + testPatients.clear(); + testPatients.push_back(singleDatasetName); + } + colReader->ClearDataElementIds(); + colReader->AddDataElementIds(testPatients); + mitk::DataCollection::Pointer testCollection = colReader->LoadCollection(testCollectionPath); + + std::time_t now; + time(&now); + double seconds = std::difftime(now, lastTimePoint); + timingFile << seconds << ";"; + time(&lastTimePoint); + + + mitk::VigraRandomForestClassifier::Pointer forest = mitk::VigraRandomForestClassifier::New(); + MITK_INFO << "Convert Test data"; + auto testDataX = mitk::DCUtilities::DC3dDToMatrixXd(testCollection, modalities, testMask); + + for (std::size_t i = 0; i < forestVector.size(); ++i) + { + forest = dynamic_cast(mitk::IOUtil::Load(forestVector[i])[0].GetPointer()); + + time(&now); + seconds = std::difftime(now, lastTimePoint); + MITK_INFO << "Duration for Training: " << seconds; + timingFile << seconds << ";"; + time(&lastTimePoint); + + MITK_INFO << "Predict Test Data"; + auto testDataNewY = forest->Predict(testDataX); + auto testDataNewProb = forest->GetPointWiseProbabilities(); + + auto maxClassValue = testDataNewProb.cols(); + std::vector names; + for (int j = 0; j < maxClassValue; ++j) + { + std::string name = resultProb + std::to_string(j); + names.push_back(name); + } + + mitk::DCUtilities::MatrixToDC3d(testDataNewY, testCollection, resultMask, testMask); + mitk::DCUtilities::MatrixToDC3d(testDataNewProb, testCollection, names, testMask); + MITK_INFO << "Converted predicted data"; + + time(&now); + seconds = std::difftime(now, lastTimePoint); + timingFile << seconds << ";"; + time(&lastTimePoint); + + ////////////////////////////////////////////////////////////////////////////// + // Save results to folder + ////////////////////////////////////////////////////////////////////////////// + MITK_INFO << "Write Result to HDD"; + mitk::CollectionWriter::ExportCollectionToFolder(testCollection, + outputFolder + "/result_collection.xml", + outputFilter); + + MITK_INFO << "Calculate Statistic...."; + ////////////////////////////////////////////////////////////////////////////// + // Calculate and Print Statistic + ////////////////////////////////////////////////////////////////////////////// + std::ofstream statisticFile; + statisticFile.open(statisticFilePath.c_str(), std::ios::app); + std::ofstream sstatisticFile; + sstatisticFile.open(statisticShortFilePath.c_str(), std::ios::app); + + mitk::CollectionStatistic stat; + stat.SetCollection(testCollection); + stat.SetClassCount(5); + stat.SetGoldName(statisticGoldStandard); + stat.SetTestName(resultMask); + stat.SetMaskName(testMask); + mitk::BinaryValueminusOneToIndexMapper mapper; + stat.SetGroundTruthValueToIndexMapper(&mapper); + stat.SetTestValueToIndexMapper(&mapper); + stat.Update(); + //stat.Print(statisticFile,sstatisticFile,statisticWithHeader, statisticShortFileLabel); + stat.Print(statisticFile, sstatisticFile, true, statisticShortFileLabel + "_"+std::to_string(i)); + statisticFile.close(); + + time(&now); + seconds = std::difftime(now, lastTimePoint); + timingFile << seconds << std::endl; + time(&lastTimePoint); + timingFile.close(); + } + } + catch (std::string s) + { + MITK_INFO << s; + return 0; + } + catch (char* s) + { + MITK_INFO << s; + } + + return 0; +} + +#endif diff --git a/Modules/Classification/CLMiniApps/CLN4.cpp b/Modules/Classification/CLMiniApps/CLN4.cpp new file mode 100644 index 0000000000..f7a65eb60f --- /dev/null +++ b/Modules/Classification/CLMiniApps/CLN4.cpp @@ -0,0 +1,121 @@ +/*=================================================================== + +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 "mitkCommandLineParser.h" +#include "mitkIOUtil.h" +#include +#include "mitkCommandLineParser.h" +#include + +#include + +int main(int argc, char* argv[]) +{ + typedef itk::Image MaskImageType; + typedef itk::Image ImageType; + typedef itk::N4BiasFieldCorrectionImageFilter < ImageType, MaskImageType, ImageType > FilterType; + + mitkCommandLineParser parser; + parser.setTitle("N4 Bias Field Correction"); + parser.setCategory("Classification Command Tools"); + parser.setDescription(""); + parser.setContributor("MBI"); + + parser.setArgumentPrefix("--", "-"); + // Add command line argument names + parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text"); + parser.addArgument("input", "i", mitkCommandLineParser::InputDirectory, "Input file:", "Input file", us::Any(), false); + parser.addArgument("mask", "m", mitkCommandLineParser::OutputFile, "Output file:", "Mask file", us::Any(), false); + parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output file:", "Output file", us::Any(), false); + + parser.addArgument("number-of-controllpoints", "noc", mitkCommandLineParser::Int, "Parameter", "The noc for the point grid size defining the B-spline estimate (default 4)", us::Any(), true); + parser.addArgument("number-of-fitting-levels", "nofl", mitkCommandLineParser::Int, "Parameter", "Number of fitting levels for the multi-scale approach (default 1)", us::Any(), true); + parser.addArgument("number-of-histogram-bins", "nofl", mitkCommandLineParser::Int, "Parameter", "number of bins defining the log input intensity histogram (default 200)", us::Any(), true); + parser.addArgument("spline-order", "so", mitkCommandLineParser::Int, "Parameter", "Define the spline order (default 3)", us::Any(), true); + parser.addArgument("winer-filter-noise", "wfn", mitkCommandLineParser::Float, "Parameter", "Noise estimate defining the Wiener filter (default 0.01)", us::Any(), true); + parser.addArgument("number-of-maximum-iterations", "nomi", mitkCommandLineParser::Int, "Parameter", "Spezifies the maximum number of iterations per run", us::Any(), true); + // ToDo: Number Of Maximum Iterations durchschleifen + + std::map parsedArgs = parser.parseArguments(argc, argv); + + // Show a help message + if (parsedArgs.count("help") || parsedArgs.count("h")) + { + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + + MaskImageType::Pointer itkMsk = MaskImageType::New(); + mitk::Image::Pointer img = mitk::IOUtil::LoadImage(parsedArgs["mask"].ToString()); + mitk::CastToItkImage(img, itkMsk); + + ImageType::Pointer itkImage = ImageType::New(); + mitk::Image::Pointer img2 = mitk::IOUtil::LoadImage(parsedArgs["input"].ToString()); + mitk::CastToItkImage(img2, itkImage); + + FilterType::Pointer filter = FilterType::New(); + filter->SetInput(itkImage); + filter->SetMaskImage(itkMsk); + + + + if (parsedArgs.count("number-of-controllpoints") > 0) + { + int variable = us::any_cast(parsedArgs["maximum-iterations"]); + MITK_INFO << "Number of controll points: " << variable; + filter->SetNumberOfControlPoints(variable); + } + if (parsedArgs.count("number-of-fitting-levels") > 0) + { + int variable = us::any_cast(parsedArgs["number-of-fitting-levels"]); + MITK_INFO << "Number of fitting levels: " << variable; + filter->SetNumberOfFittingLevels(variable); + } + if (parsedArgs.count("number-of-histogram-bins") > 0) + { + int variable = us::any_cast(parsedArgs["number-of-histogram-bins"]); + MITK_INFO << "Number of histogram bins: " << variable; + filter->SetNumberOfHistogramBins(variable); + } + if (parsedArgs.count("spline-order") > 0) + { + int variable = us::any_cast(parsedArgs["spline-order"]); + MITK_INFO << "Spline Order " << variable; + filter->SetSplineOrder(variable); + } + if (parsedArgs.count("winer-filter-noise") > 0) + { + float variable = us::any_cast(parsedArgs["winer-filter-noise"]); + MITK_INFO << "Number of histogram bins: " << variable; + filter->SetWienerFilterNoise(variable); + } + if (parsedArgs.count("number-of-maximum-iterations") > 0) + { + int variable = us::any_cast(parsedArgs["number-of-maximum-iterations"]); + MITK_INFO << "Number of Maximum Iterations: " << variable; + auto list = filter->GetMaximumNumberOfIterations(); + list.Fill(variable); + filter->SetMaximumNumberOfIterations(list); + } + + filter->Update(); + auto out = filter->GetOutput(); + mitk::Image::Pointer outImg = mitk::Image::New(); + mitk::CastToMitkImage(out, outImg); + mitk::IOUtil::Save(outImg, parsedArgs["output"].ToString()); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/Modules/Classification/CLMiniApps/CLNrrdToPoly.cpp b/Modules/Classification/CLMiniApps/CLNrrdToPoly.cpp new file mode 100644 index 0000000000..b7eb90a499 --- /dev/null +++ b/Modules/Classification/CLMiniApps/CLNrrdToPoly.cpp @@ -0,0 +1,78 @@ +/*=================================================================== + +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 mitkCLPolyToNrrd_cpp +#define mitkCLPolyToNrrd_cpp + +#include "time.h" +#include +#include + +#include +#include "mitkCommandLineParser.h" + +// VTK +#include +#include +#include + +typedef itk::Image< double, 3 > FloatImageType; +typedef itk::Image< unsigned char, 3 > MaskImageType; + + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + parser.setArgumentPrefix("--", "-"); + // required params + parser.addArgument("mask", "m", mitkCommandLineParser::InputImage, "Input Mask", "Mask Image that specifies the area over for the statistic, (Values = 1)", us::Any(), false); + parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output text file", "Target file. The output statistic is appended to this file.", us::Any(), false); + + // Miniapp Infos + parser.setCategory("Classification Tools"); + parser.setTitle("Segmentation to Mask"); + parser.setDescription("Estimates a Mesh from a segmentation"); + parser.setContributor("MBI"); + + std::map parsedArgs = parser.parseArguments(argc, argv); + + if (parsedArgs.size()==0) + { + return EXIT_FAILURE; + } + if ( parsedArgs.count("help") || parsedArgs.count("h")) + { + return EXIT_SUCCESS; + } + + MITK_INFO << "Version: "<< 1.0; + + mitk::Image::Pointer mask = mitk::IOUtil::LoadImage(parsedArgs["mask"].ToString()); + + + vtkSmartPointer image = mask->GetVtkImageData(); + image->SetOrigin(mask->GetGeometry()->GetOrigin()[0], mask->GetGeometry()->GetOrigin()[1], mask->GetGeometry()->GetOrigin()[2]); + vtkSmartPointer mesher = vtkSmartPointer::New(); + mesher->SetInputData(image); + mitk::Surface::Pointer surf = mitk::Surface::New(); + mesher->SetValue(0,0.5); + mesher->Update(); + surf->SetVtkPolyData(mesher->GetOutput()); + mitk::IOUtil::Save(surf, parsedArgs["output"].ToString()); + + return 0; +} + +#endif \ No newline at end of file diff --git a/Modules/Classification/CLMiniApps/CLOverlayRoiCenterOfMass.cpp b/Modules/Classification/CLMiniApps/CLOverlayRoiCenterOfMass.cpp new file mode 100644 index 0000000000..d4f23bca8c --- /dev/null +++ b/Modules/Classification/CLMiniApps/CLOverlayRoiCenterOfMass.cpp @@ -0,0 +1,178 @@ +/*=================================================================== + +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 mitkCLPolyToNrrd_cpp +#define mitkCLPolyToNrrd_cpp + +#include "time.h" +#include +#include + +#include +#include "mitkCommandLineParser.h" + +#include +#include +#include + +#include +#include +#include "QmitkRegisterClasses.h" +#include "QmitkRenderWindow.h" +#include "vtkRenderLargeImage.h" +#include "vtkPNGWriter.h" + +#include +#include + +typedef itk::Image< double, 3 > FloatImageType; +typedef itk::Image< unsigned char, 3 > MaskImageType; + + +template +static void +FindMostSampleSlice(itk::Image* mask, int & selectedSlice) +{ + int idx = VImageDimension - 1; + + int size = mask->GetLargestPossibleRegion().GetSize()[idx]; + std::vector numberOfSamples; + numberOfSamples.resize(size,0); + + itk::ImageRegionIteratorWithIndex > mask1Iter(mask, mask->GetLargestPossibleRegion()); + while (!mask1Iter.IsAtEnd()) + { + if (mask1Iter.Value() > 0) + { + numberOfSamples[mask1Iter.GetIndex()[idx]]+=1; + } + ++mask1Iter; + } + selectedSlice = 0; + for (std::size_t i = 0; i < numberOfSamples.size(); ++i) + { + if (numberOfSamples[selectedSlice] < numberOfSamples[i]) + selectedSlice = i; + } +} + +static +void SaveSliceOrImageAsPNG(mitk::Image::Pointer image, mitk::Image::Pointer mask, std::string path, int index) +{ + // Create a Standalone Datastorage for the single purpose of saving screenshots.. + mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); + QmitkRenderWindow renderWindow; + renderWindow.GetRenderer()->SetDataStorage(ds); + + auto nodeI = mitk::DataNode::New(); + nodeI->SetData(image); + auto nodeM = mitk::DataNode::New(); + nodeM->SetData(mask); + ds->Add(nodeI); + ds->Add(nodeM); + + mitk::TimeGeometry::Pointer geo = ds->ComputeBoundingGeometry3D(ds->GetAll()); + mitk::RenderingManager::GetInstance()->InitializeViews( + mask->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); + + mitk::SliceNavigationController::Pointer sliceNaviController = renderWindow.GetSliceNavigationController(); + sliceNaviController->SetViewDirection(mitk::SliceNavigationController::Axial); + unsigned int numberOfSteps = 1; + if (sliceNaviController) + { + numberOfSteps = sliceNaviController->GetSlice()->GetSteps(); + sliceNaviController->GetSlice()->SetPos(numberOfSteps-index); + } + + renderWindow.show(); + renderWindow.resize(256, 256); + + //if (sliceNaviController) + //{ + // sliceNaviController->GetSlice()->SetPos(index); + //} + renderWindow.GetRenderer()->PrepareRender(); + + vtkRenderWindow* renderWindow2 = renderWindow.GetVtkRenderWindow(); + mitk::BaseRenderer* baserenderer = mitk::BaseRenderer::GetInstance(renderWindow2); + auto vtkRender = baserenderer->GetVtkRenderer(); + vtkRender->GetRenderWindow()->WaitForCompletion(); + + vtkRenderLargeImage* magnifier = vtkRenderLargeImage::New(); + magnifier->SetInput(vtkRender); + magnifier->SetMagnification(3.0); + + std::stringstream ss; + ss << path <<".png"; + std::string tmpImageName; + ss >> tmpImageName; + auto fileWriter = vtkPNGWriter::New(); + fileWriter->SetInputConnection(magnifier->GetOutputPort()); + fileWriter->SetFileName(tmpImageName.c_str()); + fileWriter->Write(); + fileWriter->Delete(); +} + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + parser.setArgumentPrefix("--", "-"); + + parser.addArgument("image", "i", mitkCommandLineParser::InputImage, "Input Image", "", us::Any(),false); + parser.addArgument("mask", "m", mitkCommandLineParser::InputImage, "Input Image", "", us::Any(),false); + parser.addArgument("output", "o", mitkCommandLineParser::InputImage, "Output Image", "", us::Any(),false); + + // Miniapp Infos + parser.setCategory("Classification Tools"); + parser.setTitle("Image with Overlay Plotter"); + parser.setDescription("Plots "); + parser.setContributor("MBI"); + + std::map parsedArgs = parser.parseArguments(argc, argv); + + std::string imagePath = us::any_cast(parsedArgs["image"]); + std::string maskPath = us::any_cast(parsedArgs["mask"]); + std::string outputPath = us::any_cast(parsedArgs["output"]); + + if (parsedArgs.size()==0) + { + return EXIT_FAILURE; + } + if ( parsedArgs.count("help") || parsedArgs.count("h")) + { + return EXIT_SUCCESS; + } + + std::string version = "Version: 1.0"; + MITK_INFO << version; + + mitk::Image::Pointer image = mitk::IOUtil::LoadImage(imagePath); + mitk::Image::Pointer mask = mitk::IOUtil::LoadImage(maskPath); + + // Create a QTApplication and a Datastorage + // This is necessary in order to save screenshots of + // each image / slice. + QApplication qtapplication(argc, argv); + QmitkRegisterClasses(); + + int currentSlice = 0; + AccessByItk_1(mask, FindMostSampleSlice, currentSlice); + + SaveSliceOrImageAsPNG(image, mask, outputPath, currentSlice); + + return 0; +} + +#endif diff --git a/Modules/Classification/CLMiniApps/CLPlanarFigureToNrrd.cpp b/Modules/Classification/CLMiniApps/CLPlanarFigureToNrrd.cpp new file mode 100644 index 0000000000..df2a5c77f3 --- /dev/null +++ b/Modules/Classification/CLMiniApps/CLPlanarFigureToNrrd.cpp @@ -0,0 +1,145 @@ +/*=================================================================== + +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 mitkCLPolyToNrrd_cpp +#define mitkCLPolyToNrrd_cpp + +#include "time.h" +#include + +#include + +#include "mitkCommandLineParser.h" +#include +#include +#include +#include +#include + + +typedef itk::Image< double, 3 > FloatImageType; +typedef itk::Image< unsigned char, 3 > MaskImageType; + +struct MaskParameter +{ + mitk::Image::Pointer mask; + unsigned int axis; + unsigned int slice; +}; + +template < typename TPixel, unsigned int VImageDimension > +void CreateNewMask(const itk::Image< TPixel, VImageDimension > *image, MaskParameter param, mitk::Image::Pointer &output) +{ + int transform[3][2]; + transform[0][0] = 1; transform[0][1] = 2; + transform[1][0] = 0; transform[1][1] = 2; + transform[2][0] = 0; transform[2][1] = 1; + + typedef itk::Image MaskType; + typedef itk::Image Mask2DType; + typename Mask2DType::Pointer mask = Mask2DType::New(); + mitk::CastToItkImage(param.mask, mask); + + + typename MaskType::Pointer mask3D = MaskType::New(); + mask3D->SetRegions(image->GetLargestPossibleRegion()); + mask3D->SetSpacing(image->GetSpacing()); + mask3D->SetOrigin(image->GetOrigin()); + mask3D->Allocate(); + + itk::ImageRegionIteratorWithIndex iter(mask3D, mask3D->GetLargestPossibleRegion()); + while (!iter.IsAtEnd()) + { + auto index = iter.GetIndex(); + iter.Set(0); + if (index[param.axis] == param.slice) + { + Mask2DType::IndexType index2D; + index2D[0] = index[transform[param.axis][0]]; + index2D[1] = index[transform[param.axis][1]]; + auto pixel = mask->GetPixel(index2D); + iter.Set(pixel); + } + ++iter; + } + + mitk::CastToMitkImage(mask3D, output); + +} + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + parser.setArgumentPrefix("--", "-"); + // required params + parser.addArgument("planar", "p", mitkCommandLineParser::InputDirectory, "Input Polydata", "Path to the input VTK polydata", us::Any(), false); + parser.addArgument("image", "i", mitkCommandLineParser::OutputDirectory, "Input Image", "Image which defines the dimensions of the Segmentation", us::Any(), false); + parser.addArgument("output", "o", mitkCommandLineParser::InputFile, "Output file", "Output file. ", us::Any(), false); + // Miniapp Infos + parser.setCategory("Classification Tools"); + parser.setTitle("Planar Data to Nrrd Segmentation"); + parser.setDescription("Creates a Nrrd segmentation based on a 2d-vtk polydata."); + parser.setContributor("MBI"); + + std::map parsedArgs = parser.parseArguments(argc, argv); + + if (parsedArgs.size()==0) + { + return EXIT_FAILURE; + } + if ( parsedArgs.count("help") || parsedArgs.count("h")) + { + return EXIT_SUCCESS; + } + + try + { + mitk::BaseData::Pointer data = mitk::IOUtil::Load(parsedArgs["planar"].ToString())[0]; + mitk::PlanarFigure::Pointer planar = dynamic_cast(data.GetPointer()); + + mitk::Image::Pointer image = dynamic_cast(mitk::IOUtil::Load(parsedArgs["image"].ToString())[0].GetPointer()); + + mitk::PlanarFigureMaskGenerator::Pointer pfMaskGen = mitk::PlanarFigureMaskGenerator::New(); + pfMaskGen->SetPlanarFigure(planar); + pfMaskGen->SetTimeStep(0); + pfMaskGen->SetInputImage(image); + + mitk::Image::Pointer mask = pfMaskGen->GetMask(); + mitk::Image::Pointer refImage = pfMaskGen->GetReferenceImage(); + unsigned int axis = pfMaskGen->GetPlanarFigureAxis(); + unsigned int slice = pfMaskGen->GetPlanarFigureSlice(); + + //itk::Image::IndexType index; + mitk::Image::Pointer fullMask; + MaskParameter param; + param.slice = slice; + param.axis = axis; + param.mask = mask; + AccessByItk_2(image, CreateNewMask, param, fullMask); + + std::string saveAs = parsedArgs["output"].ToString(); + MITK_INFO << "Save as: " << saveAs; + mitk::IOUtil::Save(pfMaskGen->GetMask(), saveAs); + mitk::IOUtil::Save(fullMask, saveAs); + + return 0; + } + catch (...) + { + return EXIT_FAILURE; + } +} + +#endif diff --git a/Modules/Classification/CLMiniApps/CLPointSetToSegmentation.cpp b/Modules/Classification/CLMiniApps/CLPointSetToSegmentation.cpp new file mode 100644 index 0000000000..7e1acff682 --- /dev/null +++ b/Modules/Classification/CLMiniApps/CLPointSetToSegmentation.cpp @@ -0,0 +1,120 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ +#ifndef mitkCLPolyToNrrd_cpp +#define mitkCLPolyToNrrd_cpp + +#include "time.h" +#include + +#include +#include +#include +#include +#include + +#include "mitkCommandLineParser.h" + + + +typedef itk::Image< double, 3 > FloatImageType; +typedef itk::Image< unsigned short, 3 > MaskImageType; + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + parser.setArgumentPrefix("--", "-"); + // required params + parser.addArgument("pointset", "p", mitkCommandLineParser::InputDirectory, "Input Polydata", "Path to the input VTK polydata", us::Any(), false); + parser.addArgument("image", "i", mitkCommandLineParser::OutputDirectory, "Input Image", "Image which defines the dimensions of the Segmentation", us::Any(), false); + parser.addArgument("output", "o", mitkCommandLineParser::InputFile, "Output file", "Output files. Two files are create, a .nrrd image and a 3d-vtk.", us::Any(), false); + // Miniapp Infos + parser.setCategory("Classification Tools"); + parser.setTitle("2D-Polydata to Nrrd Segmentation"); + parser.setDescription("Creates a Nrrd segmentation based on a 2d-vtk polydata."); + parser.setContributor("MBI"); + + std::map parsedArgs = parser.parseArguments(argc, argv); + + if (parsedArgs.size()==0) + { + return EXIT_FAILURE; + } + if ( parsedArgs.count("help") || parsedArgs.count("h")) + { + return EXIT_SUCCESS; + } + + mitk::BaseData::Pointer data = mitk::IOUtil::Load(parsedArgs["pointset"].ToString())[0]; + mitk::Image::Pointer image = dynamic_cast(mitk::IOUtil::Load(parsedArgs["image"].ToString())[0].GetPointer()); + + //MITK_INFO << data; + mitk::PointSet::Pointer points = dynamic_cast(data.GetPointer()); + MaskImageType::Pointer mask = MaskImageType::New(); + mitk::CastToItkImage(image, mask); + + double minX, minY, minZ; + double maxX, maxY, maxZ; + minX = minY = minZ = std::numeric_limits::max(); + maxX = maxY = maxZ = std::numeric_limits::lowest(); + + for (auto iter = points->Begin(); iter != points->End(); ++iter) + { + minX = std::min(minX, iter.Value().GetElement(0)); + minY = std::min(minY, iter.Value().GetElement(1)); + minZ = std::min(minZ, iter.Value().GetElement(2)); + maxX = std::max(maxX, iter.Value().GetElement(0)); + maxY = std::max(maxY, iter.Value().GetElement(1)); + maxZ = std::max(maxZ, iter.Value().GetElement(2)); + } + MaskImageType::PointType point; + MaskImageType::IndexType iMin; + MaskImageType::IndexType iMax; + point[0] = minX; + point[1] = minY; + point[2] = minZ; + mask->TransformPhysicalPointToIndex(point, iMin); + point[0] = maxX; + point[1] = maxY; + point[2] = maxZ; + mask->TransformPhysicalPointToIndex(point, iMax); + + itk::ImageRegionIteratorWithIndex iter(mask, mask->GetLargestPossibleRegion()); + while (!iter.IsAtEnd()) + { + MaskImageType::IndexType index = iter.GetIndex(); + if ((index[0] >= iMin[0]) && (index[1] >= iMin[1]) && (index[2] >= iMin[2]) && + (index[0] <= iMax[0]) && (index[1] <= iMax[1]) && (index[2] <= iMax[2])) + { + iter.Set(1); + } + else + { + iter.Set(0); + } + ++iter; + } + + mitk::Image::Pointer ergImage = mitk::Image::New(); + mitk::CastToMitkImage(mask, ergImage); + + std::string saveAs = parsedArgs["output"].ToString(); + MITK_INFO << "Save as: " << saveAs; + mitk::IOUtil::Save(ergImage, saveAs); + + return 0; +} + +#endif \ No newline at end of file diff --git a/Modules/Classification/CLMiniApps/CLVoxelClassification.cpp b/Modules/Classification/CLMiniApps/CLPurfVoxelClassification.cpp similarity index 93% copy from Modules/Classification/CLMiniApps/CLVoxelClassification.cpp copy to Modules/Classification/CLMiniApps/CLPurfVoxelClassification.cpp index 949387a7cb..89d359991f 100644 --- a/Modules/Classification/CLMiniApps/CLVoxelClassification.cpp +++ b/Modules/Classification/CLMiniApps/CLPurfVoxelClassification.cpp @@ -1,437 +1,447 @@ /*=================================================================== 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 mitkForest_cpp #define mitkForest_cpp #include "time.h" #include #include #include #include #include #include #include #include #include #include #include // ----------------------- Forest Handling ---------------------- //#include #include //#include //#include //#include //#include // ----------------------- Point weighting ---------------------- //#include //#include //#include #include //#include //#include //#include //#include int main(int argc, char* argv[]) { MITK_INFO << "Starting MITK_Forest Mini-App"; + double startTime = time(0); ////////////////////////////////////////////////////////////////////////////// // Read Console Input Parameter ////////////////////////////////////////////////////////////////////////////// - ConfigFileReader allConfig(argv[2]); + ConfigFileReader allConfig(argv[1]); bool readFile = true; std::stringstream ss; for (int i = 0; i < argc; ++i ) { MITK_INFO << "-----"<< argv[i]<<"------"; if (readFile) { if (argv[i][0] == '+') { readFile = false; continue; } else { try { allConfig.ReadFile(argv[i]); } catch (std::exception &e) { MITK_INFO << e.what(); } } } else { std::string input = argv[i]; std::replace(input.begin(), input.end(),'_',' '); ss << input << std::endl; } } allConfig.ReadStream(ss); try { ////////////////////////////////////////////////////////////////////////////// // General ////////////////////////////////////////////////////////////////////////////// int currentRun = allConfig.IntValue("General","Run",0); int doTraining = allConfig.IntValue("General","Do Training",1); std::string forestPath = allConfig.Value("General","Forest Path"); std::string trainingCollectionPath = allConfig.Value("General","Patient Collection"); std::string testCollectionPath = trainingCollectionPath; - MITK_INFO << "Training collection: " << trainingCollectionPath; ////////////////////////////////////////////////////////////////////////////// // Read Default Classification ////////////////////////////////////////////////////////////////////////////// std::vector trainPatients = allConfig.Vector("Training Group",currentRun); std::vector testPatients = allConfig.Vector("Test Group",currentRun); std::vector modalities = allConfig.Vector("Modalities",0); std::string trainMask = allConfig.Value("Data","Training Mask"); std::string completeTrainMask = allConfig.Value("Data","Complete Training Mask"); std::string testMask = allConfig.Value("Data","Test Mask"); std::string resultMask = allConfig.Value("Data", "Result Mask"); std::string resultProb = allConfig.Value("Data", "Result Propability"); std::string outputFolder = allConfig.Value("General","Output Folder"); std::string writeDataFilePath = allConfig.Value("Forest","File to write data to"); ////////////////////////////////////////////////////////////////////////////// // Read Forest Parameter ////////////////////////////////////////////////////////////////////////////// int minimumSplitNodeSize = allConfig.IntValue("Forest", "Minimum split node size",1); int numberOfTrees = allConfig.IntValue("Forest", "Number of Trees",255); double samplesPerTree = atof(allConfig.Value("Forest", "Samples per Tree").c_str()); if (samplesPerTree <= 0.0000001) { samplesPerTree = 1.0; } MITK_INFO << "Samples per Tree: " << samplesPerTree; int sampleWithReplacement = allConfig.IntValue("Forest", "Sample with replacement",1); double trainPrecision = atof(allConfig.Value("Forest", "Precision").c_str()); if (trainPrecision <= 0.0000000001) { trainPrecision = 0.0; } double weightLambda = atof(allConfig.Value("Forest", "Weight Lambda").c_str()); if (weightLambda <= 0.0000000001) { weightLambda = 0.0; } int maximumTreeDepth = allConfig.IntValue("Forest", "Maximum Tree Depth",10000); - // TODO int randomSplit = allConfig.IntValue("Forest","Use RandomSplit",0); + int randomSplit = allConfig.IntValue("Forest","Use RandomSplit",0); ////////////////////////////////////////////////////////////////////////////// // Read Statistic Parameter ////////////////////////////////////////////////////////////////////////////// std::string statisticFilePath = allConfig.Value("Evaluation", "Statistic output file"); std::string statisticShortFilePath = allConfig.Value("Evaluation", "Statistic short output file"); std::string statisticShortFileLabel = allConfig.Value("Evaluation", "Index for short file"); std::string statisticGoldStandard = allConfig.Value("Evaluation", "Gold Standard Name","GTV"); - // TODO bool statisticWithHeader = allConfig.IntValue("Evaluation", "Write header in short file",0); + bool statisticWithHeader = allConfig.IntValue("Evaluation", "Write header in short file",0); std::vector labelGroupA = allConfig.Vector("LabelsA",0); std::vector labelGroupB = allConfig.Vector("LabelsB",0); ////////////////////////////////////////////////////////////////////////////// // Read Special Parameter ////////////////////////////////////////////////////////////////////////////// - // TODO bool useWeightedPoints = allConfig.IntValue("Forest", "Use point-based weighting",0); - // TODO bool writePointsToFile = allConfig.IntValue("Forest", "Write points to file",0); - // TODO int importanceWeightAlgorithm = allConfig.IntValue("Forest","Importance weight Algorithm",0); + bool useWeightedPoints = allConfig.IntValue("Forest", "Use point-based weighting",0); + bool writePointsToFile = allConfig.IntValue("Forest", "Write points to file",0); + int importanceWeightAlgorithm = allConfig.IntValue("Forest","Importance weight Algorithm",0); std::string importanceWeightName = allConfig.Value("Forest","Importance weight name",""); std::ofstream timingFile; timingFile.open((statisticFilePath + ".timing").c_str(), std::ios::app); timingFile << statisticShortFileLabel << ";"; std::time_t lastTimePoint; time(&lastTimePoint); ////////////////////////////////////////////////////////////////////////////// // Read Images ////////////////////////////////////////////////////////////////////////////// std::vector usedModalities; - for (std::size_t i = 0; i < modalities.size(); ++i) + for (int i = 0; i < modalities.size(); ++i) { usedModalities.push_back(modalities[i]); } usedModalities.push_back(trainMask); usedModalities.push_back(completeTrainMask); usedModalities.push_back(testMask); usedModalities.push_back(statisticGoldStandard); usedModalities.push_back(importanceWeightName); // vtkSmartPointer colReader = vtkSmartPointer::New(); mitk::CollectionReader* colReader = new mitk::CollectionReader(); colReader->AddDataElementIds(trainPatients); colReader->SetDataItemNames(usedModalities); //colReader->SetNames(usedModalities); mitk::DataCollection::Pointer trainCollection; if (doTraining) { trainCollection = colReader->LoadCollection(trainingCollectionPath); } colReader->ClearDataElementIds(); colReader->AddDataElementIds(testPatients); mitk::DataCollection::Pointer testCollection = colReader->LoadCollection(testCollectionPath); std::time_t now; time(&now); double seconds = std::difftime(now, lastTimePoint); timingFile << seconds << ";"; time(&lastTimePoint); /* if (writePointsToFile) { MITK_INFO << "Use external weights..."; mitk::ExternalWeighting weightReader; weightReader.SetModalities(modalities); weightReader.SetTestCollection(testCollection); weightReader.SetTrainCollection(trainCollection); weightReader.SetTestMask(testMask); weightReader.SetTrainMask(trainMask); weightReader.SetWeightsName("weights"); weightReader.SetCorrectionFactor(1.0); weightReader.SetWeightFileName(writeDataFilePath); weightReader.WriteData(); return 0; }*/ ////////////////////////////////////////////////////////////////////////////// // If required do Training.... ////////////////////////////////////////////////////////////////////////////// //mitk::DecisionForest forest; mitk::VigraRandomForestClassifier::Pointer forest = mitk::VigraRandomForestClassifier::New(); forest->SetSamplesPerTree(samplesPerTree); forest->SetMinimumSplitNodeSize(minimumSplitNodeSize); forest->SetTreeCount(numberOfTrees); forest->UseSampleWithReplacement(sampleWithReplacement); forest->SetPrecision(trainPrecision); forest->SetMaximumTreeDepth(maximumTreeDepth); forest->SetWeightLambda(weightLambda); - // TODO forest.UseRandomSplit(randomSplit); + // TOOD forest.UseRandomSplit(randomSplit); if (doTraining) { // 0 = LR-Estimation // 1 = KNN-Estimation // 2 = Kliep // 3 = Extern Image // 4 = Zadrozny // 5 = Spectral // 6 = uLSIF auto trainDataX = mitk::DCUtilities::DC3dDToMatrixXd(trainCollection, modalities, trainMask); auto trainDataY = mitk::DCUtilities::DC3dDToMatrixXi(trainCollection, trainMask, trainMask); - //if (useWeightedPoints) - if (false) + if (useWeightedPoints) + //if (false) { MITK_INFO << "Activated Point-based weighting..."; //forest.UseWeightedPoints(true); forest->UsePointWiseWeight(true); //forest.SetWeightName("calculated_weight"); /*if (importanceWeightAlgorithm == 1) { mitk::KNNDensityEstimation est; est.SetCollection(trainCollection); est.SetTrainMask(trainMask); est.SetTestMask(testMask); est.SetModalities(modalities); est.SetWeightName("calculated_weight"); est.Update(); } else if (importanceWeightAlgorithm == 2) { mitk::KliepDensityEstimation est; est.SetCollection(trainCollection); est.SetTrainMask(trainMask); est.SetTestMask(testMask); est.SetModalities(modalities); est.SetWeightName("calculated_weight"); est.Update(); } else if (importanceWeightAlgorithm == 3) { forest.SetWeightName(importanceWeightName); } else if (importanceWeightAlgorithm == 4) { mitk::ZadroznyWeighting est; est.SetCollection(trainCollection); est.SetTrainMask(trainMask); est.SetTestMask(testMask); est.SetModalities(modalities); est.SetWeightName("calculated_weight"); est.Update(); } else if (importanceWeightAlgorithm == 5) { mitk::SpectralDensityEstimation est; est.SetCollection(trainCollection); est.SetTrainMask(trainMask); est.SetTestMask(testMask); est.SetModalities(modalities); est.SetWeightName("calculated_weight"); est.Update(); } else if (importanceWeightAlgorithm == 6) { mitk::ULSIFDensityEstimation est; est.SetCollection(trainCollection); est.SetTrainMask(trainMask); est.SetTestMask(testMask); est.SetModalities(modalities); est.SetWeightName("calculated_weight"); est.Update(); } else*/ { mitk::LRDensityEstimation est; est.SetCollection(trainCollection); est.SetTrainMask(trainMask); est.SetTestMask(testMask); est.SetModalities(modalities); est.SetWeightName("calculated_weight"); est.Update(); } auto trainDataW = mitk::DCUtilities::DC3dDToMatrixXd(trainCollection, "calculated_weight", trainMask); forest->SetPointWiseWeight(trainDataW); forest->UsePointWiseWeight(true); } forest->Train(trainDataX, trainDataY); // TODO forest.Save(forestPath); } else { // TODO forest.Load(forestPath); } time(&now); seconds = std::difftime(now, lastTimePoint); timingFile << seconds << ";"; time(&lastTimePoint); ////////////////////////////////////////////////////////////////////////////// // If required do Save Forest.... ////////////////////////////////////////////////////////////////////////////// //writer.// (forest); /* auto w = forest->GetTreeWeights(); w(0,0) = 10; forest->SetTreeWeights(w);*/ - mitk::IOUtil::Save(forest,"d:/tmp/forest.forest"); + //mitk::IOUtil::Save(forest,"d:/tmp/forest.forest"); ////////////////////////////////////////////////////////////////////////////// // If required do test ////////////////////////////////////////////////////////////////////////////// auto testDataX = mitk::DCUtilities::DC3dDToMatrixXd(testCollection,modalities, testMask); auto testDataNewY = forest->Predict(testDataX); + auto testDataNewProb = forest->GetPointWiseProbabilities(); //MITK_INFO << testDataNewY; + std::vector names; + names.push_back("prob-1"); + names.push_back("prob-2"); + mitk::DCUtilities::MatrixToDC3d(testDataNewY, testCollection, resultMask, testMask); + mitk::DCUtilities::MatrixToDC3d(testDataNewProb, testCollection, names, testMask); //forest.SetMaskName(testMask); //forest.SetCollection(testCollection); //forest.Test(); //forest.PrintTree(0); time(&now); seconds = std::difftime(now, lastTimePoint); timingFile << seconds << ";"; time(&lastTimePoint); ////////////////////////////////////////////////////////////////////////////// // Cost-based analysis ////////////////////////////////////////////////////////////////////////////// // TODO Reactivate //MITK_INFO << "Calculate Cost-based Statistic "; //mitk::CostingStatistic costStat; //costStat.SetCollection(testCollection); //costStat.SetCombinedA("combinedHealty"); //costStat.SetCombinedB("combinedTumor"); //costStat.SetCombinedLabel("combinedLabel"); //costStat.SetMaskName(testMask); ////std::vector labelHealthy; ////labelHealthy.push_back("result_prop_Class-0"); ////labelHealthy.push_back("result_prop_Class-4"); ////std::vector labelTumor; ////labelTumor.push_back("result_prop_Class-1"); ////labelTumor.push_back("result_prop_Class-2"); ////labelTumor.push_back("result_prop_Class-3"); //costStat.SetProbabilitiesA(labelGroupA); //costStat.SetProbabilitiesB(labelGroupB); //std::ofstream costStatisticFile; //costStatisticFile.open((statisticFilePath + ".cost").c_str(), std::ios::app); //std::ofstream lcostStatisticFile; //lcostStatisticFile.open((statisticFilePath + ".longcost").c_str(), std::ios::app); //costStat.WriteStatistic(lcostStatisticFile,costStatisticFile,2.5,statisticShortFileLabel); //costStatisticFile.close(); //costStat.CalculateClass(50); ////////////////////////////////////////////////////////////////////////////// // Save results to folder ////////////////////////////////////////////////////////////////////////////// std::vector outputFilter; //outputFilter.push_back(resultMask); //std::vector propNames = forest.GetListOfProbabilityNames(); //outputFilter.insert(outputFilter.begin(), propNames.begin(), propNames.end()); mitk::CollectionWriter::ExportCollectionToFolder(testCollection, outputFolder + "/result_collection.xml", outputFilter); MITK_INFO << "Calculate Statistic...." ; ////////////////////////////////////////////////////////////////////////////// // Calculate and Print Statistic ////////////////////////////////////////////////////////////////////////////// std::ofstream statisticFile; statisticFile.open(statisticFilePath.c_str(), std::ios::app); std::ofstream sstatisticFile; sstatisticFile.open(statisticShortFilePath.c_str(), std::ios::app); mitk::CollectionStatistic stat; stat.SetCollection(testCollection); stat.SetClassCount(2); stat.SetGoldName(statisticGoldStandard); stat.SetTestName(resultMask); stat.SetMaskName(testMask); + mitk::BinaryValueminusOneToIndexMapper* mapper = new mitk::BinaryValueminusOneToIndexMapper; + stat.SetGroundTruthValueToIndexMapper(mapper); + stat.SetTestValueToIndexMapper(mapper); stat.Update(); //stat.Print(statisticFile,sstatisticFile,statisticWithHeader, statisticShortFileLabel); stat.Print(statisticFile,sstatisticFile,true, statisticShortFileLabel); statisticFile.close(); + delete mapper; time(&now); seconds = std::difftime(now, lastTimePoint); timingFile << seconds << std::endl; time(&lastTimePoint); timingFile.close(); } catch (std::string s) { MITK_INFO << s; return 0; } catch (char* s) { MITK_INFO << s; } return 0; } -#endif +#endif \ No newline at end of file diff --git a/Modules/Classification/CLMiniApps/CLRandomSampling.cpp b/Modules/Classification/CLMiniApps/CLRandomSampling.cpp new file mode 100644 index 0000000000..fd8632de2a --- /dev/null +++ b/Modules/Classification/CLMiniApps/CLRandomSampling.cpp @@ -0,0 +1,158 @@ +/*=================================================================== + +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 "mitkCommandLineParser.h" +#include "mitkIOUtil.h" +#include + +static std::vector splitDouble(std::string str, char delimiter) { + std::vector internal; + std::stringstream ss(str); // Turn the string into a stream. + std::string tok; + double val; + while (getline(ss, tok, delimiter)) { + std::stringstream s2(tok); + s2 >> val; + internal.push_back(val); + } + + return internal; +} + +static std::vector splitUInt(std::string str, char delimiter) { + std::vector internal; + std::stringstream ss(str); // Turn the string into a stream. + std::string tok; + unsigned int val; + while (getline(ss, tok, delimiter)) { + std::stringstream s2(tok); + s2 >> val; + internal.push_back(val); + } + + return internal; +} + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + + parser.setTitle("Random Sampling"); + parser.setCategory("Classification Command Tools"); + parser.setDescription(""); + parser.setContributor("MBI"); + + parser.setArgumentPrefix("--", "-"); + // Add command line argument names + parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text"); + parser.addArgument("input", "i", mitkCommandLineParser::InputDirectory, "Input file:", "Input file", us::Any(), false); + parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output file:", "Output file", us::Any(), false); + + parser.addArgument("single-rate", "sr", mitkCommandLineParser::OutputFile, "Single Acceptance rate for all voxel", "Output file", us::Any(), true); + parser.addArgument("class-rate", "cr", mitkCommandLineParser::OutputFile, "Class-dependend acceptance rate", "Output file", us::Any(), true); + parser.addArgument("single-number", "sn", mitkCommandLineParser::OutputFile, "Single Number of Voxel for each class", "Output file", us::Any(), true); + parser.addArgument("class-number", "cn", mitkCommandLineParser::OutputFile, "Class-dependedn number of voxels ", "Output file", us::Any(), true); + + std::map parsedArgs = parser.parseArguments(argc, argv); + + if (parsedArgs.size() == 0) + return EXIT_FAILURE; + + // Show a help message + if (parsedArgs.count("help") || parsedArgs.count("h")) + { + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + + if (parsedArgs.count("single-rate") + parsedArgs.count("class-rate") + parsedArgs.count("single-number") + parsedArgs.count("class-number") < 1) + { + std::cout << "Please specify the sampling rate or number of voxels to be labeled" << std::endl << std::endl; + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + + if (parsedArgs.count("single-rate") + parsedArgs.count("class-rate") + parsedArgs.count("single-number") + parsedArgs.count("class-number") > 2) + { + std::cout << "Please specify only one way for the sampling rate or number of voxels to be labeled" << std::endl << std::endl; + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + + + std::string inputName = us::any_cast(parsedArgs["input"]); + std::string outputName = us::any_cast(parsedArgs["output"]); + mitk::Image::Pointer image = mitk::IOUtil::LoadImage(inputName); + + mitk::RandomImageSampler::Pointer filter = mitk::RandomImageSampler::New(); + filter->SetInput(image); + + if (parsedArgs.count("single-rate")) + { + filter->SetSamplingMode(mitk::RandomImageSamplerMode::SINGLE_ACCEPTANCE_RATE); + auto rate = splitDouble(parsedArgs["single-rate"].ToString(), ';'); + if (rate.size() != 1) + { + std::cout << "Please specify a single double value for single-rate, for example 0.3." << std::endl << std::endl; + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + filter->SetAcceptanceRate(rate[0]); + } + + else if (parsedArgs.count("class-rate")) + { + filter->SetSamplingMode(mitk::RandomImageSamplerMode::CLASS_DEPENDEND_ACCEPTANCE_RATE); + auto rate = splitDouble(parsedArgs["class-rate"].ToString(), ';'); + if (rate.size() < 2) + { + std::cout << "Please specify at least two, semicolon separated values for class-rate, for example '0.3;0.2' ." << std::endl << std::endl; + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + filter->SetAcceptanceRateVector(rate); + } + + else if (parsedArgs.count("single-number")) + { + filter->SetSamplingMode(mitk::RandomImageSamplerMode::SINGLE_NUMBER_OF_ACCEPTANCE); + auto rate = splitUInt(parsedArgs["single-number"].ToString(), ';'); + if (rate.size() != 1) + { + std::cout << "Please specify a single double value for single-number, for example 100." << std::endl << std::endl; + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + filter->SetNumberOfSamples(rate[0]); + } + + else if (parsedArgs.count("class-number")) + { + filter->SetSamplingMode(mitk::RandomImageSamplerMode::CLASS_DEPENDEND_NUMBER_OF_ACCEPTANCE); + auto rate = splitUInt(parsedArgs["class-number"].ToString(), ';'); + if (rate.size() < 2) + { + std::cout << "Please specify at least two, semicolon separated values for class-number, for example '100;200' ." << std::endl << std::endl; + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + filter->SetNumberOfSamplesVector(rate); + } + filter->Update(); + mitk::IOUtil::Save(filter->GetOutput(), outputName); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/Modules/Classification/CLMiniApps/CLRemoveEmptyVoxels.cpp b/Modules/Classification/CLMiniApps/CLRemoveEmptyVoxels.cpp new file mode 100644 index 0000000000..45d34b1ecf --- /dev/null +++ b/Modules/Classification/CLMiniApps/CLRemoveEmptyVoxels.cpp @@ -0,0 +1,159 @@ +/*=================================================================== + +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 "mitkCommandLineParser.h" +#include "mitkIOUtil.h" +#include "mitkImageCast.h" +#include +#include +#include "mitkImageGenerator.h" + +int main(int argc, char* argv[]) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskImageType; + typedef ImageType::Pointer ImagePointerType; + typedef MaskImageType::Pointer MaskImagePointerType; + + typedef itk::ImageRegionConstIterator ConstIteratorType; + typedef itk::ImageRegionConstIterator ConstMaskIteratorType; + typedef itk::ImageRegionIterator IteratorType; + typedef itk::ImageRegionIterator MaskIteratorType; + + mitkCommandLineParser parser; + + parser.setTitle("Remove empty voxels Sampling"); + parser.setCategory("Classification Command Tools"); + parser.setDescription(""); + parser.setContributor("MBI"); + + parser.setArgumentPrefix("--", "-"); + // Add command line argument names + parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text"); + parser.addArgument("mask-input", "mi", mitkCommandLineParser::InputDirectory, "Input file:", "Input file", us::Any(), false); + parser.addArgument("mask-output", "mo", mitkCommandLineParser::OutputFile, "Output file:", "Output file", us::Any(), false); + + for (int i = 0; i < 100; ++i) + { + std::stringstream s1; s1 << i; std::string number = s1.str(); + parser.addArgument("input"+number, "i"+number, mitkCommandLineParser::OutputFile, "Input file", "input file", us::Any(), true); + parser.addArgument("output" + number, "o" + number, mitkCommandLineParser::OutputFile, "Output File", "Output file", us::Any(), true); + } + + std::map parsedArgs = parser.parseArguments(argc, argv); + + if (parsedArgs.size() == 0) + return EXIT_FAILURE; + + // Show a help message + if (parsedArgs.count("help") || parsedArgs.count("h")) + { + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + + // Load Mask Image and count number of non-zero voxels + mitk::Image::Pointer mask = mitk::IOUtil::LoadImage(parsedArgs["mask-input"].ToString()); + MaskImagePointerType itkMask = MaskImageType::New(); + mitk::CastToItkImage(mask, itkMask); + ConstMaskIteratorType maskIter(itkMask, itkMask->GetLargestPossibleRegion()); + std::size_t nonZero = 0; + while (!maskIter.IsAtEnd()) + { + if (maskIter.Value() > 0) + { + ++nonZero; + } + ++maskIter; + } + maskIter.GoToBegin(); + + // Create new mask image + auto mitkNewMask = mitk::ImageGenerator::GenerateGradientImage(nonZero, 1, 1, 1, 1, 1); + MaskImagePointerType itkNewMask = MaskImageType::New(); + mitk::CastToItkImage(mitkNewMask, itkNewMask); + MaskIteratorType newMaskIter(itkNewMask, itkNewMask->GetLargestPossibleRegion()); + + // Read additional image + std::vector mitkImagesVector; + std::vector itkImageVector; + std::vector itkOutputImageVector; + std::vector inputIteratorVector; + std::vector outputIteratorVector; + for (int i = 0; i < 100; ++i) + { + std::stringstream s1; s1 << i; std::string number = s1.str(); + if (parsedArgs.count("input" + number) < 1) + break; + if (parsedArgs.count("output" + number) < 1) + break; + + mitk::Image::Pointer image = mitk::IOUtil::LoadImage(parsedArgs["input"+number].ToString()); + mitkImagesVector.push_back(image); + + ImagePointerType itkImage = ImageType::New(); + mitk::CastToItkImage(image, itkImage); + itkImageVector.push_back(itkImage); + + ConstIteratorType iter(itkImage, itkImage->GetLargestPossibleRegion()); + inputIteratorVector.push_back(iter); + + auto mitkNewImage = mitk::ImageGenerator::GenerateGradientImage(nonZero, 1, 1, 1, 1, 1); + ImagePointerType itkNewOutput = ImageType::New(); + mitk::CastToItkImage(mitkNewImage, itkNewOutput); + IteratorType outputIter(itkNewOutput, itkNewOutput->GetLargestPossibleRegion()); + itkOutputImageVector.push_back(itkNewOutput); + outputIteratorVector.push_back(outputIter); + } + + // Convert non-zero voxels to the new images + while (!maskIter.IsAtEnd()) + { + if (maskIter.Value() > 0) + { + newMaskIter.Set(maskIter.Value()); + ++newMaskIter; + for (std::size_t i = 0; i < outputIteratorVector.size(); ++i) + { + outputIteratorVector[i].Set(inputIteratorVector[i].Value()); + ++(outputIteratorVector[i]); + } + } + ++maskIter; + for (std::size_t i = 0; i < inputIteratorVector.size(); ++i) + { + ++(inputIteratorVector[i]); + } + + } + + // Save the new images + for (std::size_t i = 0; i < outputIteratorVector.size(); ++i) + { + std::stringstream s1; s1 << i; std::string number = s1.str(); + mitk::Image::Pointer mitkImage = mitk::Image::New(); + mitk::CastToMitkImage(itkOutputImageVector[i], mitkImage); + mitk::IOUtil::Save(mitkImage, parsedArgs["output" + number].ToString()); + } + // Save the new mask + { + mitk::Image::Pointer mitkImage = mitk::Image::New(); + mitk::CastToMitkImage(itkNewMask, mitkImage); + mitk::IOUtil::Save(mitkImage, parsedArgs["mask-output"].ToString()); + } + + return EXIT_SUCCESS; +} diff --git a/Modules/Classification/CLMiniApps/CLResampleImageToReference.cpp b/Modules/Classification/CLMiniApps/CLResampleImageToReference.cpp new file mode 100644 index 0000000000..e58acfba58 --- /dev/null +++ b/Modules/Classification/CLMiniApps/CLResampleImageToReference.cpp @@ -0,0 +1,116 @@ +/*=================================================================== + +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 mitkCLResampleImageToReference_cpp +#define mitkCLResampleImageToReference_cpp + +#include "mitkCommandLineParser.h" +#include +#include +#include +#include +#include +#include + +// ITK +#include "itkLinearInterpolateImageFunction.h" +#include "itkWindowedSincInterpolateImageFunction.h" +#include "itkNearestNeighborInterpolateImageFunction.h" +#include "itkIdentityTransform.h" +#include "itkResampleImageFilter.h" + + +template +void +ResampleImageToReferenceFunction(itk::Image* itkReference, mitk::Image::Pointer moving, std::string ergPath) +{ + typedef itk::Image InputImageType; + + // Identify Transform + typedef itk::IdentityTransform T_Transform; + typename T_Transform::Pointer _pTransform = T_Transform::New(); + _pTransform->SetIdentity(); + + typedef itk::WindowedSincInterpolateImageFunction< InputImageType, VImageDimension> WindowedSincInterpolatorType; + typename WindowedSincInterpolatorType::Pointer sinc_interpolator = WindowedSincInterpolatorType::New(); + + typedef itk::LinearInterpolateImageFunction< InputImageType> LinearInterpolateImageFunctionType; + typename LinearInterpolateImageFunctionType::Pointer lin_interpolator = LinearInterpolateImageFunctionType::New(); + + typedef itk::NearestNeighborInterpolateImageFunction< InputImageType> NearestNeighborInterpolateImageFunctionType; + typename NearestNeighborInterpolateImageFunctionType::Pointer nn_interpolator = NearestNeighborInterpolateImageFunctionType::New(); + + typename InputImageType::Pointer itkMoving = InputImageType::New(); + mitk::CastToItkImage(moving,itkMoving); + typedef itk::ResampleImageFilter ResampleFilterType; + + typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); + resampler->SetInput(itkMoving); + resampler->SetReferenceImage( itkReference ); + resampler->UseReferenceImageOn(); + resampler->SetTransform(_pTransform); + //if ( sincInterpol) + // resampler->SetInterpolator(sinc_interpolator); + //else + resampler->SetInterpolator(lin_interpolator); + + resampler->Update(); + + // Convert back to mitk + mitk::Image::Pointer result = mitk::Image::New(); + result->InitializeByItk(resampler->GetOutput()); + GrabItkImageMemory(resampler->GetOutput(), result); + MITK_INFO << "writing result to: " << ergPath; + mitk::IOUtil::Save(result, ergPath); + //return result; +} + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + parser.setArgumentPrefix("--", "-"); + // required params + parser.addArgument("fix", "f", mitkCommandLineParser::InputImage, "Input Image", "Path to the input VTK polydata", us::Any(), false); + parser.addArgument("moving", "m", mitkCommandLineParser::OutputFile, "Output text file", "Target file. The output statistic is appended to this file.", us::Any(), false); + parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Extension", "File extension. Default is .nii.gz", us::Any(), false); + + // Miniapp Infos + parser.setCategory("Classification Tools"); + parser.setTitle("Resample Image To Reference"); + parser.setDescription("Resamples an image (moving) to an given image (fix) without additional registration."); + parser.setContributor("MBI"); + + std::map parsedArgs = parser.parseArguments(argc, argv); + + if (parsedArgs.size() == 0) + { + return EXIT_FAILURE; + } + if (parsedArgs.count("help") || parsedArgs.count("h")) + { + return EXIT_SUCCESS; + } + + mitk::Image::Pointer fix = mitk::IOUtil::LoadImage(parsedArgs["fix"].ToString()); + mitk::Image::Pointer moving = mitk::IOUtil::LoadImage(parsedArgs["moving"].ToString()); + mitk::Image::Pointer erg = mitk::Image::New(); + + AccessByItk_2(fix, ResampleImageToReferenceFunction, moving, parsedArgs["output"].ToString()); + +} + + + +#endif diff --git a/Modules/Classification/CLMiniApps/CLScreenshot.cpp b/Modules/Classification/CLMiniApps/CLScreenshot.cpp new file mode 100644 index 0000000000..e16aa2156e --- /dev/null +++ b/Modules/Classification/CLMiniApps/CLScreenshot.cpp @@ -0,0 +1,171 @@ +/*=================================================================== + +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 mitkCLPolyToNrrd_cpp +#define mitkCLPolyToNrrd_cpp + +#include "time.h" +#include +#include + +#include +#include "mitkCommandLineParser.h" + +#include +#include + +#include +#include +#include "QmitkRegisterClasses.h" +#include "QmitkRenderWindow.h" +#include "vtkRenderLargeImage.h" +#include "vtkPNGWriter.h" + + +static +void SaveSliceOrImageAsPNG(std::vector listOfOutputs, std::string path) +{ + std::vector colorList; + colorList.push_back(mitk::ColorProperty::New(0.9569, 0.16471, 0.25490)); // Red + colorList.push_back(mitk::ColorProperty::New(1, 0.839, 0)); // Yellow + colorList.push_back(mitk::ColorProperty::New(0, 0.6, 0.2)); // Green + colorList.push_back(mitk::ColorProperty::New(0, 0.2784, 0.7255)); // BLue + colorList.push_back(mitk::ColorProperty::New(1,0.3608,0)); // Orange + colorList.push_back(mitk::ColorProperty::New(0.839215,0.141176,0.80784)); // Violett + colorList.push_back(mitk::ColorProperty::New(0.1372,0.81568,0.7647)); // Turkis + colorList.push_back(mitk::ColorProperty::New(0.61176,0.9568,0.16078)); // Bright Green + colorList.push_back(mitk::ColorProperty::New(1,0.4274,0.16862)); // Dark Orange + colorList.push_back(mitk::ColorProperty::New(0.88633,0.14901,0.64705)); // Pink + + // Create a Standalone Datastorage for the single purpose of saving screenshots.. + mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); + QmitkRenderWindow renderWindow; + renderWindow.GetRenderer()->SetDataStorage(ds); + + int numberOfSegmentations = 0; + bool isSegmentation = false; + for (auto name : listOfOutputs) + { + mitk::Image::Pointer tmpImage = mitk::IOUtil::LoadImage(name); + auto nodeI = mitk::DataNode::New(); + nodeI->SetData(tmpImage); + nodeI->GetPropertyValue("binary",isSegmentation); + if (isSegmentation) + { + nodeI->SetProperty("color", colorList[numberOfSegmentations % colorList.size()]); + nodeI->SetProperty("binaryimage.hoveringannotationcolor", colorList[numberOfSegmentations % colorList.size()]); + nodeI->SetProperty("binaryimage.hoveringcolor", colorList[numberOfSegmentations % colorList.size()]); + nodeI->SetProperty("binaryimage.selectedannotationcolor", colorList[numberOfSegmentations % colorList.size()]); + nodeI->SetProperty("binaryimage.selectedcolor", colorList[numberOfSegmentations % colorList.size()]); + numberOfSegmentations++; + } + ds->Add(nodeI); + } + + mitk::TimeGeometry::Pointer geo = ds->ComputeBoundingGeometry3D(ds->GetAll()); + mitk::RenderingManager::GetInstance()->InitializeViews(geo); + + mitk::SliceNavigationController::Pointer sliceNaviController = renderWindow.GetSliceNavigationController(); + unsigned int numberOfSteps = 1; + if (sliceNaviController) + { + numberOfSteps = sliceNaviController->GetSlice()->GetSteps(); + sliceNaviController->GetSlice()->SetPos(0); + } + + renderWindow.show(); + renderWindow.resize(512, 512); + + for (unsigned int currentStep = 0; currentStep < numberOfSteps; ++currentStep) + { + if (sliceNaviController) + { + sliceNaviController->GetSlice()->SetPos(currentStep); + } + + renderWindow.GetRenderer()->PrepareRender(); + + vtkRenderWindow* renderWindow2 = renderWindow.GetVtkRenderWindow(); + mitk::BaseRenderer* baserenderer = mitk::BaseRenderer::GetInstance(renderWindow2); + auto vtkRender = baserenderer->GetVtkRenderer(); + vtkRender->GetRenderWindow()->WaitForCompletion(); + + vtkRenderLargeImage* magnifier = vtkRenderLargeImage::New(); + magnifier->SetInput(vtkRender); + magnifier->SetMagnification(3.0); + + std::stringstream ss; + ss << path << "screenshot_step-"<> tmpImageName; + auto fileWriter = vtkPNGWriter::New(); + fileWriter->SetInputConnection(magnifier->GetOutputPort()); + fileWriter->SetFileName(tmpImageName.c_str()); + fileWriter->Write(); + fileWriter->Delete(); + } +} + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + parser.setArgumentPrefix("--", "-"); + + // Required Parameter + parser.addArgument("image", "i", mitkCommandLineParser::InputImage, "Input Image", "Path to the input image files (Separated with semicolons)", us::Any(), false); + parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output text file", "Path to output file. The output statistic is appended to this file.", us::Any(), false); + parser.addArgument("direction", "dir", mitkCommandLineParser::String, "Int", "Allows to specify the direction for Cooc and RL. 0: All directions, 1: Only single direction (Test purpose), 2,3,4... Without dimension 0,1,2... ", us::Any()); + + // Miniapp Infos + parser.setCategory("Classification Tools"); + parser.setTitle("Screenshot of a single image"); + parser.setDescription(""); + parser.setContributor("MBI"); + + std::map parsedArgs = parser.parseArguments(argc, argv); + + if (parsedArgs.size()==0) + { + return EXIT_FAILURE; + } + if ( parsedArgs.count("help") || parsedArgs.count("h")) + { + return EXIT_SUCCESS; + } + + std::string version = "Version: 1.0"; + MITK_INFO << version; + + //int direction = 0; + if (parsedArgs.count("direction")) + { + MITK_INFO << "Warning: Option direction currently not supported"; + // direction = mitk::cl::splitDouble(parsedArgs["direction"].ToString(), ';')[0]; + } + + auto listOfFiles = mitk::cl::splitString(parsedArgs["image"].ToString(), ';'); + + // Create a QTApplication and a Datastorage + // This is necessary in order to save screenshots of + // each image / slice. + QApplication qtapplication(argc, argv); + QmitkRegisterClasses(); + + SaveSliceOrImageAsPNG(listOfFiles, parsedArgs["output"].ToString()); + + return 0; +} + +#endif diff --git a/Modules/Classification/CLMiniApps/CLSkullMask.cpp b/Modules/Classification/CLMiniApps/CLSkullMask.cpp new file mode 100644 index 0000000000..04e3d4dce2 --- /dev/null +++ b/Modules/Classification/CLMiniApps/CLSkullMask.cpp @@ -0,0 +1,198 @@ +/*=================================================================== + +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 mitkCLPolyToNrrd_cpp +#define mitkCLPolyToNrrd_cpp + +#include "time.h" +#include +#include + +#include +#include "mitkCommandLineParser.h" + +#include "itkImageRegionIterator.h" +// MITK +#include +#include +#include +// ITK +#include +#include + +typedef itk::Image< double, 3 > FloatImageType; +typedef itk::Image< unsigned char, 3 > MaskImageType; + +template +static void + DetectSkull(itk::Image* itkImage, mitk::Image::Pointer im2, mitk::Image::Pointer mask1, std::string output) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskType; + + typename ImageType::Pointer itkIm2 = ImageType::New(); + typename MaskType::Pointer itkMask1 = MaskType::New(); + mitk::CastToItkImage(im2, itkIm2); + mitk::CastToItkImage(mask1, itkMask1); + + itk::ImageRegionIterator iterI1(itkImage, itkImage->GetLargestPossibleRegion()); + itk::ImageRegionIterator iterI2(itkIm2, itkImage->GetLargestPossibleRegion()); + itk::ImageRegionIterator iter(itkMask1, itkImage->GetLargestPossibleRegion()); + while (! iter.IsAtEnd()) + { + unsigned char maskV = 0; + if (iterI1.Value() > 0.0001 && iterI2.Value() > 0.00001) + maskV = 1; + iter.Set(maskV); + ++iter; + ++iterI1; + ++iterI2; + } + + mitk::Image::Pointer img = mitk::ImportItkImage(itkMask1); + mitk::IOUtil::Save(img, output); +} + +int main(int argc, char* argv[]) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskType; + + + mitkCommandLineParser parser; + parser.setArgumentPrefix("--", "-"); + // required params + parser.addArgument("image", "i", mitkCommandLineParser::StringList, "Input Image", "Path to the input images. Mask covers area of all images", us::Any(), false); + parser.addArgument("mask", "m", mitkCommandLineParser::InputImage, "Input Mask", "The median of the area covered by this mask will be set to 1", us::Any(), false); + + // Miniapp Infos + parser.setCategory("Classification Tools"); + parser.setTitle("MR Normalization Tool"); + parser.setDescription("Normalizes a MR image. Sets the Median of the tissue covered by mask 0 to 0 and the median of the area covered by mask 1 to 1."); + parser.setContributor("MBI"); + + std::map parsedArgs = parser.parseArguments(argc, argv); + + if (parsedArgs.size()==0) + { + return EXIT_FAILURE; + } + if ( parsedArgs.count("help") || parsedArgs.count("h")) + { + return EXIT_SUCCESS; + } + us::Any listAny = parsedArgs["image"]; + auto inputImageList = us::any_cast(listAny); + + std::vector imageList; + for (std::size_t i = 0; i < inputImageList.size(); ++i) + { + mitk::Image::Pointer image = dynamic_cast(mitk::IOUtil::Load(inputImageList[i])[0].GetPointer()); + ImageType::Pointer itkImage = ImageType::New(); + mitk::CastToItkImage(image, itkImage); + imageList.push_back(itkImage); + } + mitk::Image::Pointer mitkMask = dynamic_cast(mitk::IOUtil::Load(inputImageList[0])[0].GetPointer()); + MaskType::Pointer mask = MaskType::New(); + mitk::CastToItkImage(mitkMask, mask); + + itk::ImageRegionIterator maskIter(mask, mask->GetLargestPossibleRegion()); + while (!maskIter.IsAtEnd()) + { + maskIter.Set(0); + ++maskIter; + } + + std::vector listOfIndexes; + listOfIndexes.reserve(1000); + + // Find Start Location for the case that one corner is "blocked" by content. Works only on the first image! + ImageType::IndexType tmpIndex; + ImageType::IndexType startIndex; + startIndex.Fill(0); + for (unsigned char i = 0; i < 8; ++i) + { + tmpIndex.Fill(0); + if ((i & 1) > 0) tmpIndex[0] = mask->GetLargestPossibleRegion().GetSize(0)-1; + if ((i & 2) > 0) tmpIndex[1] = mask->GetLargestPossibleRegion().GetSize(1)-1; + if ((i & 4) > 0) tmpIndex[2] = mask->GetLargestPossibleRegion().GetSize(2)-1; + + MITK_INFO << tmpIndex; + if (imageList[0]->GetPixel(tmpIndex) < imageList[0]->GetPixel(startIndex)) + { + startIndex = tmpIndex; + } + } + listOfIndexes.push_back(tmpIndex); + + while (listOfIndexes.size() > 0) + { + ImageType::IndexType currentIndex = listOfIndexes.back(); + listOfIndexes.pop_back(); + if (!(mask->GetLargestPossibleRegion().IsInside(currentIndex))) + { + continue; + } + if (mask->GetPixel(currentIndex) == 0) + { + mask->SetPixel(currentIndex, 1); + double minimum = std::numeric_limits::max(); + for (std::size_t i = 0; i < imageList.size(); ++i) + { + minimum = std::min(minimum, imageList[i]->GetPixel(currentIndex)); + } + if (minimum < 35) + { + mask->SetPixel(currentIndex, 2); + tmpIndex = currentIndex; + tmpIndex[0] += 1; + listOfIndexes.push_back(tmpIndex); + tmpIndex[0] -= 2; + listOfIndexes.push_back(tmpIndex); + tmpIndex[0] += 1; + tmpIndex[1] += 1; + listOfIndexes.push_back(tmpIndex); + tmpIndex[1] -= 2; + listOfIndexes.push_back(tmpIndex); + tmpIndex[1] += 1; + tmpIndex[2] += 1; + listOfIndexes.push_back(tmpIndex); + tmpIndex[2] -= 2; + listOfIndexes.push_back(tmpIndex); + } + } + } + MITK_INFO << "Im here"; + maskIter.GoToBegin(); + while (!maskIter.IsAtEnd()) + { + if (maskIter.Get() == 2) + maskIter.Set(0); + else + maskIter.Set(1); + ++maskIter; + } + + mitk::Image::Pointer ergMask = mitk::ImportItkImage(mask); + + std::string maskPath = parsedArgs["mask"].ToString(); + mitk::IOUtil::Save(ergMask, maskPath); + + //AccessByItk_3(image, Normalize, im2, mask, parsedArgs["output"].ToString()); + + return 0; +} + +#endif \ No newline at end of file diff --git a/Modules/Classification/CLMiniApps/CLVoxelClassification.cpp b/Modules/Classification/CLMiniApps/CLVoxelClassification.cpp index 949387a7cb..1a29727ccb 100644 --- a/Modules/Classification/CLMiniApps/CLVoxelClassification.cpp +++ b/Modules/Classification/CLMiniApps/CLVoxelClassification.cpp @@ -1,437 +1,480 @@ /*=================================================================== 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 mitkForest_cpp #define mitkForest_cpp #include "time.h" #include #include #include #include #include #include #include #include #include #include #include // ----------------------- Forest Handling ---------------------- //#include #include //#include //#include //#include //#include // ----------------------- Point weighting ---------------------- //#include //#include //#include #include //#include //#include //#include //#include int main(int argc, char* argv[]) { MITK_INFO << "Starting MITK_Forest Mini-App"; ////////////////////////////////////////////////////////////////////////////// // Read Console Input Parameter ////////////////////////////////////////////////////////////////////////////// - ConfigFileReader allConfig(argv[2]); + ConfigFileReader allConfig(argv[1]); bool readFile = true; std::stringstream ss; for (int i = 0; i < argc; ++i ) { MITK_INFO << "-----"<< argv[i]<<"------"; if (readFile) { if (argv[i][0] == '+') { readFile = false; continue; } else { try { allConfig.ReadFile(argv[i]); } catch (std::exception &e) { MITK_INFO << e.what(); } } } else { std::string input = argv[i]; std::replace(input.begin(), input.end(),'_',' '); ss << input << std::endl; } } allConfig.ReadStream(ss); try { ////////////////////////////////////////////////////////////////////////////// // General ////////////////////////////////////////////////////////////////////////////// int currentRun = allConfig.IntValue("General","Run",0); int doTraining = allConfig.IntValue("General","Do Training",1); std::string forestPath = allConfig.Value("General","Forest Path"); std::string trainingCollectionPath = allConfig.Value("General","Patient Collection"); - std::string testCollectionPath = trainingCollectionPath; - MITK_INFO << "Training collection: " << trainingCollectionPath; + std::string testCollectionPath = allConfig.Value("General", "Patient Test Collection", trainingCollectionPath); ////////////////////////////////////////////////////////////////////////////// // Read Default Classification ////////////////////////////////////////////////////////////////////////////// std::vector trainPatients = allConfig.Vector("Training Group",currentRun); std::vector testPatients = allConfig.Vector("Test Group",currentRun); - std::vector modalities = allConfig.Vector("Modalities",0); + std::vector modalities = allConfig.Vector("Modalities", 0); + std::vector outputFilter = allConfig.Vector("Output Filter", 0); std::string trainMask = allConfig.Value("Data","Training Mask"); std::string completeTrainMask = allConfig.Value("Data","Complete Training Mask"); std::string testMask = allConfig.Value("Data","Test Mask"); std::string resultMask = allConfig.Value("Data", "Result Mask"); std::string resultProb = allConfig.Value("Data", "Result Propability"); std::string outputFolder = allConfig.Value("General","Output Folder"); std::string writeDataFilePath = allConfig.Value("Forest","File to write data to"); + ////////////////////////////////////////////////////////////////////////////// + // Read Data Forest Parameter + ////////////////////////////////////////////////////////////////////////////// + int testSingleDataset = allConfig.IntValue("Data", "Test Single Dataset",0); + std::string singleDatasetName = allConfig.Value("Data", "Single Dataset Name", "none"); + int trainSingleDataset = allConfig.IntValue("Data", "Train Single Dataset", 0); + std::string singleTrainDatasetName = allConfig.Value("Data", "Train Single Dataset Name", "none"); + ////////////////////////////////////////////////////////////////////////////// // Read Forest Parameter ////////////////////////////////////////////////////////////////////////////// int minimumSplitNodeSize = allConfig.IntValue("Forest", "Minimum split node size",1); int numberOfTrees = allConfig.IntValue("Forest", "Number of Trees",255); double samplesPerTree = atof(allConfig.Value("Forest", "Samples per Tree").c_str()); if (samplesPerTree <= 0.0000001) { samplesPerTree = 1.0; } MITK_INFO << "Samples per Tree: " << samplesPerTree; int sampleWithReplacement = allConfig.IntValue("Forest", "Sample with replacement",1); double trainPrecision = atof(allConfig.Value("Forest", "Precision").c_str()); if (trainPrecision <= 0.0000000001) { trainPrecision = 0.0; } double weightLambda = atof(allConfig.Value("Forest", "Weight Lambda").c_str()); if (weightLambda <= 0.0000000001) { weightLambda = 0.0; } int maximumTreeDepth = allConfig.IntValue("Forest", "Maximum Tree Depth",10000); // TODO int randomSplit = allConfig.IntValue("Forest","Use RandomSplit",0); ////////////////////////////////////////////////////////////////////////////// // Read Statistic Parameter ////////////////////////////////////////////////////////////////////////////// std::string statisticFilePath = allConfig.Value("Evaluation", "Statistic output file"); std::string statisticShortFilePath = allConfig.Value("Evaluation", "Statistic short output file"); std::string statisticShortFileLabel = allConfig.Value("Evaluation", "Index for short file"); std::string statisticGoldStandard = allConfig.Value("Evaluation", "Gold Standard Name","GTV"); // TODO bool statisticWithHeader = allConfig.IntValue("Evaluation", "Write header in short file",0); std::vector labelGroupA = allConfig.Vector("LabelsA",0); std::vector labelGroupB = allConfig.Vector("LabelsB",0); ////////////////////////////////////////////////////////////////////////////// // Read Special Parameter ////////////////////////////////////////////////////////////////////////////// - // TODO bool useWeightedPoints = allConfig.IntValue("Forest", "Use point-based weighting",0); + bool useWeightedPoints = allConfig.IntValue("Forest", "Use point-based weighting",0); // TODO bool writePointsToFile = allConfig.IntValue("Forest", "Write points to file",0); // TODO int importanceWeightAlgorithm = allConfig.IntValue("Forest","Importance weight Algorithm",0); std::string importanceWeightName = allConfig.Value("Forest","Importance weight name",""); std::ofstream timingFile; timingFile.open((statisticFilePath + ".timing").c_str(), std::ios::app); timingFile << statisticShortFileLabel << ";"; std::time_t lastTimePoint; time(&lastTimePoint); ////////////////////////////////////////////////////////////////////////////// // Read Images ////////////////////////////////////////////////////////////////////////////// std::vector usedModalities; for (std::size_t i = 0; i < modalities.size(); ++i) { usedModalities.push_back(modalities[i]); } usedModalities.push_back(trainMask); usedModalities.push_back(completeTrainMask); usedModalities.push_back(testMask); usedModalities.push_back(statisticGoldStandard); usedModalities.push_back(importanceWeightName); - // vtkSmartPointer colReader = vtkSmartPointer::New(); + if (trainSingleDataset > 0) + { + trainPatients.clear(); + trainPatients.push_back(singleTrainDatasetName); + } + mitk::CollectionReader* colReader = new mitk::CollectionReader(); colReader->AddDataElementIds(trainPatients); colReader->SetDataItemNames(usedModalities); //colReader->SetNames(usedModalities); mitk::DataCollection::Pointer trainCollection; if (doTraining) { trainCollection = colReader->LoadCollection(trainingCollectionPath); } + + if (testSingleDataset > 0) + { + testPatients.clear(); + testPatients.push_back(singleDatasetName); + } colReader->ClearDataElementIds(); colReader->AddDataElementIds(testPatients); mitk::DataCollection::Pointer testCollection = colReader->LoadCollection(testCollectionPath); std::time_t now; time(&now); double seconds = std::difftime(now, lastTimePoint); timingFile << seconds << ";"; time(&lastTimePoint); /* if (writePointsToFile) { MITK_INFO << "Use external weights..."; mitk::ExternalWeighting weightReader; weightReader.SetModalities(modalities); weightReader.SetTestCollection(testCollection); weightReader.SetTrainCollection(trainCollection); weightReader.SetTestMask(testMask); weightReader.SetTrainMask(trainMask); weightReader.SetWeightsName("weights"); weightReader.SetCorrectionFactor(1.0); weightReader.SetWeightFileName(writeDataFilePath); weightReader.WriteData(); return 0; }*/ ////////////////////////////////////////////////////////////////////////////// // If required do Training.... ////////////////////////////////////////////////////////////////////////////// //mitk::DecisionForest forest; mitk::VigraRandomForestClassifier::Pointer forest = mitk::VigraRandomForestClassifier::New(); forest->SetSamplesPerTree(samplesPerTree); forest->SetMinimumSplitNodeSize(minimumSplitNodeSize); forest->SetTreeCount(numberOfTrees); forest->UseSampleWithReplacement(sampleWithReplacement); forest->SetPrecision(trainPrecision); forest->SetMaximumTreeDepth(maximumTreeDepth); forest->SetWeightLambda(weightLambda); // TODO forest.UseRandomSplit(randomSplit); if (doTraining) { // 0 = LR-Estimation // 1 = KNN-Estimation // 2 = Kliep // 3 = Extern Image // 4 = Zadrozny // 5 = Spectral // 6 = uLSIF auto trainDataX = mitk::DCUtilities::DC3dDToMatrixXd(trainCollection, modalities, trainMask); auto trainDataY = mitk::DCUtilities::DC3dDToMatrixXi(trainCollection, trainMask, trainMask); - //if (useWeightedPoints) - if (false) + if (useWeightedPoints) + //if (false) { MITK_INFO << "Activated Point-based weighting..."; //forest.UseWeightedPoints(true); forest->UsePointWiseWeight(true); //forest.SetWeightName("calculated_weight"); /*if (importanceWeightAlgorithm == 1) { mitk::KNNDensityEstimation est; est.SetCollection(trainCollection); est.SetTrainMask(trainMask); est.SetTestMask(testMask); est.SetModalities(modalities); est.SetWeightName("calculated_weight"); est.Update(); } else if (importanceWeightAlgorithm == 2) { mitk::KliepDensityEstimation est; est.SetCollection(trainCollection); est.SetTrainMask(trainMask); est.SetTestMask(testMask); est.SetModalities(modalities); est.SetWeightName("calculated_weight"); est.Update(); } else if (importanceWeightAlgorithm == 3) { forest.SetWeightName(importanceWeightName); } else if (importanceWeightAlgorithm == 4) { mitk::ZadroznyWeighting est; est.SetCollection(trainCollection); est.SetTrainMask(trainMask); est.SetTestMask(testMask); est.SetModalities(modalities); est.SetWeightName("calculated_weight"); est.Update(); } else if (importanceWeightAlgorithm == 5) { mitk::SpectralDensityEstimation est; est.SetCollection(trainCollection); est.SetTrainMask(trainMask); est.SetTestMask(testMask); est.SetModalities(modalities); est.SetWeightName("calculated_weight"); est.Update(); } else if (importanceWeightAlgorithm == 6) { mitk::ULSIFDensityEstimation est; est.SetCollection(trainCollection); est.SetTrainMask(trainMask); est.SetTestMask(testMask); est.SetModalities(modalities); est.SetWeightName("calculated_weight"); est.Update(); } else*/ { mitk::LRDensityEstimation est; est.SetCollection(trainCollection); est.SetTrainMask(trainMask); est.SetTestMask(testMask); est.SetModalities(modalities); est.SetWeightName("calculated_weight"); est.Update(); } auto trainDataW = mitk::DCUtilities::DC3dDToMatrixXd(trainCollection, "calculated_weight", trainMask); forest->SetPointWiseWeight(trainDataW); forest->UsePointWiseWeight(true); } + MITK_INFO << "Start training the forest"; forest->Train(trainDataX, trainDataY); - // TODO forest.Save(forestPath); + + MITK_INFO << "Save Forest"; + mitk::IOUtil::Save(forest, forestPath); } else { - // TODO forest.Load(forestPath); + forest = dynamic_cast(mitk::IOUtil::Load(forestPath)[0].GetPointer());// TODO forest.Load(forestPath); } time(&now); seconds = std::difftime(now, lastTimePoint); + MITK_INFO << "Duration for Training: " << seconds; timingFile << seconds << ";"; time(&lastTimePoint); ////////////////////////////////////////////////////////////////////////////// // If required do Save Forest.... ////////////////////////////////////////////////////////////////////////////// //writer.// (forest); /* auto w = forest->GetTreeWeights(); w(0,0) = 10; forest->SetTreeWeights(w);*/ - mitk::IOUtil::Save(forest,"d:/tmp/forest.forest"); ////////////////////////////////////////////////////////////////////////////// // If required do test ////////////////////////////////////////////////////////////////////////////// + MITK_INFO << "Convert Test data"; auto testDataX = mitk::DCUtilities::DC3dDToMatrixXd(testCollection,modalities, testMask); + + MITK_INFO << "Predict Test Data"; auto testDataNewY = forest->Predict(testDataX); + auto testDataNewProb = forest->GetPointWiseProbabilities(); //MITK_INFO << testDataNewY; + auto maxClassValue = testDataNewProb.cols(); + std::vector names; + for (int i = 0; i < maxClassValue; ++i) + { + std::string name = resultProb + std::to_string(i); + MITK_INFO << name; + names.push_back(name); + } + //names.push_back("prob-1"); + //names.push_back("prob-2"); + mitk::DCUtilities::MatrixToDC3d(testDataNewY, testCollection, resultMask, testMask); + mitk::DCUtilities::MatrixToDC3d(testDataNewProb, testCollection, names, testMask); + MITK_INFO << "Converted predicted data"; //forest.SetMaskName(testMask); //forest.SetCollection(testCollection); //forest.Test(); //forest.PrintTree(0); time(&now); seconds = std::difftime(now, lastTimePoint); timingFile << seconds << ";"; time(&lastTimePoint); ////////////////////////////////////////////////////////////////////////////// // Cost-based analysis ////////////////////////////////////////////////////////////////////////////// // TODO Reactivate //MITK_INFO << "Calculate Cost-based Statistic "; //mitk::CostingStatistic costStat; //costStat.SetCollection(testCollection); //costStat.SetCombinedA("combinedHealty"); //costStat.SetCombinedB("combinedTumor"); //costStat.SetCombinedLabel("combinedLabel"); //costStat.SetMaskName(testMask); ////std::vector labelHealthy; ////labelHealthy.push_back("result_prop_Class-0"); ////labelHealthy.push_back("result_prop_Class-4"); ////std::vector labelTumor; ////labelTumor.push_back("result_prop_Class-1"); ////labelTumor.push_back("result_prop_Class-2"); ////labelTumor.push_back("result_prop_Class-3"); //costStat.SetProbabilitiesA(labelGroupA); //costStat.SetProbabilitiesB(labelGroupB); //std::ofstream costStatisticFile; //costStatisticFile.open((statisticFilePath + ".cost").c_str(), std::ios::app); //std::ofstream lcostStatisticFile; //lcostStatisticFile.open((statisticFilePath + ".longcost").c_str(), std::ios::app); //costStat.WriteStatistic(lcostStatisticFile,costStatisticFile,2.5,statisticShortFileLabel); //costStatisticFile.close(); //costStat.CalculateClass(50); ////////////////////////////////////////////////////////////////////////////// // Save results to folder ////////////////////////////////////////////////////////////////////////////// - std::vector outputFilter; + ////std::vector outputFilter; //outputFilter.push_back(resultMask); //std::vector propNames = forest.GetListOfProbabilityNames(); //outputFilter.insert(outputFilter.begin(), propNames.begin(), propNames.end()); + MITK_INFO << "Write Result to HDD"; mitk::CollectionWriter::ExportCollectionToFolder(testCollection, outputFolder + "/result_collection.xml", outputFilter); MITK_INFO << "Calculate Statistic...." ; ////////////////////////////////////////////////////////////////////////////// // Calculate and Print Statistic ////////////////////////////////////////////////////////////////////////////// std::ofstream statisticFile; statisticFile.open(statisticFilePath.c_str(), std::ios::app); std::ofstream sstatisticFile; sstatisticFile.open(statisticShortFilePath.c_str(), std::ios::app); mitk::CollectionStatistic stat; stat.SetCollection(testCollection); - stat.SetClassCount(2); + stat.SetClassCount(5); stat.SetGoldName(statisticGoldStandard); stat.SetTestName(resultMask); stat.SetMaskName(testMask); + mitk::BinaryValueminusOneToIndexMapper mapper; + stat.SetGroundTruthValueToIndexMapper(&mapper); + stat.SetTestValueToIndexMapper(&mapper); stat.Update(); //stat.Print(statisticFile,sstatisticFile,statisticWithHeader, statisticShortFileLabel); stat.Print(statisticFile,sstatisticFile,true, statisticShortFileLabel); statisticFile.close(); time(&now); seconds = std::difftime(now, lastTimePoint); timingFile << seconds << std::endl; time(&lastTimePoint); timingFile.close(); } catch (std::string s) { MITK_INFO << s; return 0; } catch (char* s) { MITK_INFO << s; } return 0; } #endif diff --git a/Modules/Classification/CLMiniApps/CLVoxelFeatures.cpp b/Modules/Classification/CLMiniApps/CLVoxelFeatures.cpp index 02a115a1a6..ef6f66995c 100644 --- a/Modules/Classification/CLMiniApps/CLVoxelFeatures.cpp +++ b/Modules/Classification/CLMiniApps/CLVoxelFeatures.cpp @@ -1,326 +1,434 @@ /*=================================================================== 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 mitkCLVoxeFeatures_cpp #define mitkCLVoxeFeatures_cpp #include "time.h" #include #include #include #include #include #include "mitkCommandLineParser.h" -#include -#include - #include "itkDiscreteGaussianImageFilter.h" #include #include "itkHessianRecursiveGaussianImageFilter.h" #include "itkUnaryFunctorImageFilter.h" -#include #include "vnl/algo/vnl_symmetric_eigensystem.h" +#include #include +#include static std::vector splitDouble(std::string str, char delimiter) { std::vector internal; std::stringstream ss(str); // Turn the string into a stream. std::string tok; double val; while (std::getline(ss, tok, delimiter)) { std::stringstream s2(tok); s2 >> val; internal.push_back(val); } return internal; } namespace Functor { template class MatrixFirstEigenvalue { public: MatrixFirstEigenvalue() {} virtual ~MatrixFirstEigenvalue() {} int order; inline TOutput operator ()(const TInput& input) { double a,b,c; if (input[0] < 0.01 && input[1] < 0.01 &&input[2] < 0.01 &&input[3] < 0.01 &&input[4] < 0.01 &&input[5] < 0.01) return 0; vnl_symmetric_eigensystem_compute_eigenvals(input[0], input[1],input[2],input[3],input[4],input[5],a,b,c); switch (order) { case 0: return a; case 1: return b; case 2: return c; default: return a; } } bool operator !=(const MatrixFirstEigenvalue) const { return false; } bool operator ==(const MatrixFirstEigenvalue& other) const { return !(*this != other); } }; } template void GaussianFilter(itk::Image* itkImage, double variance, mitk::Image::Pointer &output) { typedef itk::Image ImageType; typedef itk::DiscreteGaussianImageFilter< ImageType, ImageType > GaussFilterType; typename GaussFilterType::Pointer gaussianFilter = GaussFilterType::New(); gaussianFilter->SetInput( itkImage ); gaussianFilter->SetVariance(variance); gaussianFilter->Update(); mitk::CastToMitkImage(gaussianFilter->GetOutput(), output); } template void DifferenceOfGaussFilter(itk::Image* itkImage, double variance, mitk::Image::Pointer &output) { typedef itk::Image ImageType; typedef itk::DiscreteGaussianImageFilter< ImageType, ImageType > GaussFilterType; typedef itk::SubtractImageFilter SubFilterType; typename GaussFilterType::Pointer gaussianFilter1 = GaussFilterType::New(); gaussianFilter1->SetInput( itkImage ); gaussianFilter1->SetVariance(variance); gaussianFilter1->Update(); typename GaussFilterType::Pointer gaussianFilter2 = GaussFilterType::New(); gaussianFilter2->SetInput( itkImage ); gaussianFilter2->SetVariance(variance*0.66*0.66); gaussianFilter2->Update(); typename SubFilterType::Pointer subFilter = SubFilterType::New(); subFilter->SetInput1(gaussianFilter1->GetOutput()); subFilter->SetInput2(gaussianFilter2->GetOutput()); subFilter->Update(); mitk::CastToMitkImage(subFilter->GetOutput(), output); } template void LaplacianOfGaussianFilter(itk::Image* itkImage, double variance, mitk::Image::Pointer &output) { typedef itk::Image ImageType; typedef itk::DiscreteGaussianImageFilter< ImageType, ImageType > GaussFilterType; typedef itk::LaplacianRecursiveGaussianImageFilter LaplacianFilter; typename GaussFilterType::Pointer gaussianFilter = GaussFilterType::New(); gaussianFilter->SetInput( itkImage ); gaussianFilter->SetVariance(variance); gaussianFilter->Update(); typename LaplacianFilter::Pointer laplaceFilter = LaplacianFilter::New(); laplaceFilter->SetInput(gaussianFilter->GetOutput()); laplaceFilter->Update(); mitk::CastToMitkImage(laplaceFilter->GetOutput(), output); } template void HessianOfGaussianFilter(itk::Image* itkImage, double variance, std::vector &out) { typedef itk::Image ImageType; typedef itk::Image FloatImageType; typedef itk::HessianRecursiveGaussianImageFilter HessianFilterType; typedef typename HessianFilterType::OutputImageType VectorImageType; typedef Functor::MatrixFirstEigenvalue DeterminantFunctorType; typedef itk::UnaryFunctorImageFilter DetFilterType; typename HessianFilterType::Pointer hessianFilter = HessianFilterType::New(); hessianFilter->SetInput(itkImage); hessianFilter->SetSigma(std::sqrt(variance)); for (unsigned int i = 0; i < VImageDimension; ++i) { typename DetFilterType::Pointer detFilter = DetFilterType::New(); detFilter->SetInput(hessianFilter->GetOutput()); detFilter->GetFunctor().order = i; detFilter->Update(); mitk::CastToMitkImage(detFilter->GetOutput(), out[i]); } } +template +void +LocalHistograms2(itk::Image* itkImage, std::vector &out, std::vector params) +{ + typedef itk::Image ImageType; + typedef itk::MultiHistogramFilter MultiHistogramType; + + double minimum = params[0]; + double maximum = params[1]; + int bins = std::round(params[2]); + + double offset = minimum; + double delta = (maximum - minimum) / bins; + + typename MultiHistogramType::Pointer filter = MultiHistogramType::New(); + filter->SetInput(itkImage); + filter->SetOffset(offset); + filter->SetDelta(delta); + filter->SetBins(bins); + filter->Update(); + for (int i = 0; i < bins; ++i) + { + mitk::Image::Pointer img = mitk::Image::New(); + mitk::CastToMitkImage(filter->GetOutput(i), img); + out.push_back(img); + } +} + template void LocalHistograms(itk::Image* itkImage, std::vector &out, double offset, double delta) { typedef itk::Image ImageType; typedef itk::MultiHistogramFilter MultiHistogramType; typename MultiHistogramType::Pointer filter = MultiHistogramType::New(); filter->SetInput(itkImage); filter->SetOffset(offset); filter->SetDelta(delta); filter->Update(); - for (unsigned int i = 0; i < VImageDimension; ++i) + for (int i = 0; i < 11; ++i) + { + mitk::Image::Pointer img = mitk::Image::New(); + mitk::CastToMitkImage(filter->GetOutput(i), img); + out.push_back(img); + } +} + +template +void +localStatistic(itk::Image* itkImage, std::vector &out, int size) +{ + typedef itk::Image ImageType; + typedef itk::LocalStatisticFilter MultiHistogramType; + + typename MultiHistogramType::Pointer filter = MultiHistogramType::New(); + filter->SetInput(itkImage); + filter->SetSize(size); + filter->Update(); + for (int i = 0; i < 5; ++i) { mitk::Image::Pointer img = mitk::Image::New(); - mitk::CastToMitkImage(filter->GetOutput(), img); + mitk::CastToMitkImage(filter->GetOutput(i), img); out.push_back(img); } } + int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setArgumentPrefix("--", "-"); // required params parser.addArgument("image", "i", mitkCommandLineParser::InputImage, "Input Image", "Path to the input VTK polydata", us::Any(), false); parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output text file", "Target file. The output statistic is appended to this file.", us::Any(), false); + parser.addArgument("extension", "e", mitkCommandLineParser::OutputFile, "Extension", "File extension. Default is .nii.gz", us::Any(), true); parser.addArgument("gaussian","g",mitkCommandLineParser::String, "Gaussian Filtering of the input images", "Gaussian Filter. Followed by the used variances seperated by ';' ",us::Any()); parser.addArgument("difference-of-gaussian","dog",mitkCommandLineParser::String, "Difference of Gaussian Filtering of the input images", "Difference of Gaussian Filter. Followed by the used variances seperated by ';' ",us::Any()); parser.addArgument("laplace-of-gauss","log",mitkCommandLineParser::String, "Laplacian of Gaussian Filtering", "Laplacian of Gaussian Filter. Followed by the used variances seperated by ';' ",us::Any()); parser.addArgument("hessian-of-gauss","hog",mitkCommandLineParser::String, "Hessian of Gaussian Filtering", "Hessian of Gaussian Filter. Followed by the used variances seperated by ';' ",us::Any()); parser.addArgument("local-histogram", "lh", mitkCommandLineParser::String, "Local Histograms", "Calculate the local histogram based feature. Specify Offset and Delta, for exampel -3;0.6 ", us::Any()); + parser.addArgument("local-histogram2", "lh2", mitkCommandLineParser::String, "Local Histograms", "Calculate the local histogram based feature. Specify Minimum;Maximum;Bins, for exampel -3;3;6 ", us::Any()); + parser.addArgument("local-statistic", "ls", mitkCommandLineParser::String, "Local Histograms", "Calculate the local histogram based feature. Specify Offset and Delta, for exampel -3;0.6 ", us::Any()); // Miniapp Infos parser.setCategory("Classification Tools"); parser.setTitle("Global Image Feature calculator"); parser.setDescription("Calculates different global statistics for a given segmentation / image combination"); parser.setContributor("MBI"); std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) { return EXIT_FAILURE; } if ( parsedArgs.count("help") || parsedArgs.count("h")) { return EXIT_SUCCESS; } mitk::Image::Pointer image = dynamic_cast(mitk::IOUtil::Load(parsedArgs["image"].ToString())[0].GetPointer()); std::string filename=parsedArgs["output"].ToString(); + std::string extension = ".nii.gz"; + if (parsedArgs.count("extension")) + { + extension = parsedArgs["extension"].ToString(); + } + //////////////////////////////////////////////////////////////// - // CAlculate Gaussian Features + // CAlculate Local Histogram //////////////////////////////////////////////////////////////// MITK_INFO << "Check for Local Histogram..."; if (parsedArgs.count("local-histogram")) { std::vector outs; auto ranges = splitDouble(parsedArgs["local-histogram"].ToString(), ';'); if (ranges.size() < 2) { MITK_INFO << "Missing Delta and Offset for Local Histogram"; } else { AccessByItk_3(image, LocalHistograms, outs, ranges[0], ranges[1]); for (std::size_t i = 0; i < outs.size(); ++i) { - std::string name = filename + "-lh" + us::any_value_to_string(i)+".nii.gz"; + std::string name = filename + "-lh" + us::any_value_to_string(i)+extension; + mitk::IOUtil::Save(outs[i], name); + } + } + } + + //////////////////////////////////////////////////////////////// + // CAlculate Local Histogram 2 + //////////////////////////////////////////////////////////////// + MITK_INFO << "Check for Local Histogram..."; + if (parsedArgs.count("local-histogram2")) + { + std::vector outs; + auto ranges = splitDouble(parsedArgs["local-histogram2"].ToString(), ';'); + if (ranges.size() < 3) + { + MITK_INFO << "Missing Delta and Offset for Local Histogram"; + } + else + { + AccessByItk_2(image, LocalHistograms2, outs, ranges); + for (std::size_t i = 0; i < outs.size(); ++i) + { + std::string name = filename + "-lh2" + us::any_value_to_string(i)+extension; mitk::IOUtil::Save(outs[i], name); } } } + //////////////////////////////////////////////////////////////// + // CAlculate Local Statistic + //////////////////////////////////////////////////////////////// + MITK_INFO << "Check for Local Histogram..."; + if (parsedArgs.count("local-statistic")) + { + std::vector outs; + auto ranges = splitDouble(parsedArgs["local-statistic"].ToString(), ';'); + if (ranges.size() < 1) + { + MITK_INFO << "Missing Rage"; + } + else + { + for (std::size_t j = 0; j < ranges.size(); ++j) + { + AccessByItk_2(image, localStatistic, outs, ranges[j]); + for (std::size_t i = 0; i < outs.size(); ++i) + { + std::string name = filename + "-lstat" + us::any_value_to_string(ranges[j])+ "_" +us::any_value_to_string(i)+extension; + mitk::IOUtil::Save(outs[i], name); + } + outs.clear(); + } + } + } + + //////////////////////////////////////////////////////////////// // CAlculate Gaussian Features //////////////////////////////////////////////////////////////// MITK_INFO << "Check for Gaussian..."; if (parsedArgs.count("gaussian")) { MITK_INFO << "Calculate Gaussian... " << parsedArgs["gaussian"].ToString(); auto ranges = splitDouble(parsedArgs["gaussian"].ToString(),';'); for (std::size_t i = 0; i < ranges.size(); ++i) { + MITK_INFO << "Gaussian with sigma: " << ranges[i]; mitk::Image::Pointer output; AccessByItk_2(image, GaussianFilter, ranges[i], output); - std::string name = filename + "-gaussian-" + us::any_value_to_string(ranges[i])+".nii.gz"; + MITK_INFO << "Write output:"; + std::string name = filename + "-gaussian-" + us::any_value_to_string(ranges[i]) + extension; mitk::IOUtil::Save(output, name); } } //////////////////////////////////////////////////////////////// // CAlculate Difference of Gaussian Features //////////////////////////////////////////////////////////////// MITK_INFO << "Check for DoG..."; if (parsedArgs.count("difference-of-gaussian")) { MITK_INFO << "Calculate Difference of Gaussian... " << parsedArgs["difference-of-gaussian"].ToString(); auto ranges = splitDouble(parsedArgs["difference-of-gaussian"].ToString(),';'); for (std::size_t i = 0; i < ranges.size(); ++i) { mitk::Image::Pointer output; AccessByItk_2(image, DifferenceOfGaussFilter, ranges[i], output); - std::string name = filename + "-dog-" + us::any_value_to_string(ranges[i])+".nii.gz"; + std::string name = filename + "-dog-" + us::any_value_to_string(ranges[i]) + extension; mitk::IOUtil::Save(output, name); } } MITK_INFO << "Check for LoG..."; //////////////////////////////////////////////////////////////// // CAlculate Laplacian Of Gauss Features //////////////////////////////////////////////////////////////// if (parsedArgs.count("laplace-of-gauss")) { MITK_INFO << "Calculate LoG... " << parsedArgs["laplace-of-gauss"].ToString(); auto ranges = splitDouble(parsedArgs["laplace-of-gauss"].ToString(),';'); for (std::size_t i = 0; i < ranges.size(); ++i) { mitk::Image::Pointer output; AccessByItk_2(image, LaplacianOfGaussianFilter, ranges[i], output); - std::string name = filename + "-log-" + us::any_value_to_string(ranges[i])+".nii.gz"; + std::string name = filename + "-log-" + us::any_value_to_string(ranges[i]) + extension; mitk::IOUtil::Save(output, name); } } MITK_INFO << "Check for HoG..."; //////////////////////////////////////////////////////////////// // CAlculate Hessian Of Gauss Features //////////////////////////////////////////////////////////////// if (parsedArgs.count("hessian-of-gauss")) { MITK_INFO << "Calculate HoG... " << parsedArgs["hessian-of-gauss"].ToString(); auto ranges = splitDouble(parsedArgs["hessian-of-gauss"].ToString(),';'); for (std::size_t i = 0; i < ranges.size(); ++i) { std::vector outs; outs.push_back(mitk::Image::New()); outs.push_back(mitk::Image::New()); outs.push_back(mitk::Image::New()); AccessByItk_2(image, HessianOfGaussianFilter, ranges[i], outs); - std::string name = filename + "-hog0-" + us::any_value_to_string(ranges[i])+".nii.gz"; + std::string name = filename + "-hog0-" + us::any_value_to_string(ranges[i]) + extension; mitk::IOUtil::Save(outs[0], name); - name = filename + "-hog1-" + us::any_value_to_string(ranges[i])+".nii.gz"; + name = filename + "-hog1-" + us::any_value_to_string(ranges[i]) + extension; mitk::IOUtil::Save(outs[1], name); - name = filename + "-hog2-" + us::any_value_to_string(ranges[i])+".nii.gz"; + name = filename + "-hog2-" + us::any_value_to_string(ranges[i]) + extension; mitk::IOUtil::Save(outs[2], name); } } return 0; } #endif diff --git a/Modules/Classification/CLMiniApps/CLWeighting.cpp b/Modules/Classification/CLMiniApps/CLWeighting.cpp new file mode 100644 index 0000000000..b47bcc67e3 --- /dev/null +++ b/Modules/Classification/CLMiniApps/CLWeighting.cpp @@ -0,0 +1,217 @@ +/*=================================================================== + +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 mitkForest_cpp +#define mitkForest_cpp + +#include "time.h" +#include +#include +#include "mitkCommandLineParser.h" +#include +#include +#include + + +//#include +#include + +struct TrainingSet { + vnl_matrix feature; + vnl_vector label; + double ratio; +}; + + +const +vnl_matrix ReadMatrix(std::string path) +{ + std::fstream file(path); + std::vector > listOfRows; + + std::string line; + while (std::getline(file, line)) + { + auto current_row = mitk::cl::splitDouble(line, ','); + if (listOfRows.size() < 1) + { + listOfRows.push_back(current_row); + } + else if (current_row.size() == listOfRows[0].size()) + { + listOfRows.push_back(current_row); + } + } + file.close(); + + vnl_matrix result(listOfRows.size(), listOfRows[0].size()); + for (std::size_t i = 0; i < listOfRows.size(); ++i) + { + for (std::size_t j = 0; j < listOfRows[0].size(); ++j) + { + result(i, j) = listOfRows[i][j]; + } + } + return result; +} + +const +TrainingSet ReadBothMatrix(std::string train, std::string test) +{ + std::fstream file(train); + std::vector > listOfRows; + std::vector label; + + double trSamples = 0; + double teSamples = 0; + + std::string line; + while (std::getline(file, line)) + { + auto current_row = mitk::cl::splitDouble(line, ','); + if (listOfRows.size() < 1) + { + listOfRows.push_back(current_row); + label.push_back(0); + trSamples += 1; + } + else if (current_row.size() == listOfRows[0].size()) + { + listOfRows.push_back(current_row); + label.push_back(0); + trSamples += 1; + } + } + file.close(); + + std::fstream file2(test); + while (std::getline(file2, line)) + { + auto current_row = mitk::cl::splitDouble(line, ','); + if (listOfRows.size() < 1) + { + listOfRows.push_back(current_row); + label.push_back(1); + teSamples += 1; + } + else if (current_row.size() == listOfRows[0].size()) + { + listOfRows.push_back(current_row); + label.push_back(1); + teSamples += 1; + } + } + file2.close(); + + vnl_matrix resultMatrix(listOfRows.size(), listOfRows[0].size()); + vnl_vector resultLabel(listOfRows.size()); + for (std::size_t i = 0; i < listOfRows.size(); ++i) + { + for (std::size_t j = 0; j < listOfRows[0].size(); ++j) + { + resultMatrix(i, j) = listOfRows[i][j]; + } + resultLabel(i) = label[i]; + } + TrainingSet set; + set.feature = resultMatrix; + set.label = resultLabel; + set.ratio = trSamples / teSamples; + return set; +} + + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + parser.setArgumentPrefix("--", "-"); + // required params + parser.addArgument("training", "t", mitkCommandLineParser::InputImage, "Input Image", "desc", us::Any(), false); + parser.addArgument("prediction", "p", mitkCommandLineParser::InputImage, "Input Image", "desc", us::Any(), false); + parser.addArgument("output", "o", mitkCommandLineParser::InputImage, "Normalisation mode", "desc", us::Any(), false); + //parser.addArgument("algorithm", "a", mitkCommandLineParser::InputImage, "Input Mask", "desc", us::Any(), false); + + // Miniapp Infos + parser.setCategory("Classification Tools"); + parser.setTitle("Importance weighting algorithm"); + parser.setDescription("Calculates the importance weighting of two input matrixes. "); + parser.setContributor("MBI"); + + MITK_INFO << "Extracting Parameters...."; + std::map parsedArgs = parser.parseArguments(argc, argv); + + std::string trainingPath = us::any_cast(parsedArgs["training"]); + std::string predictionPath = us::any_cast(parsedArgs["prediction"]); + std::string outputPath = us::any_cast(parsedArgs["output"]); + //std::string algorithm = us::any_cast(parsedArgs["algorithm"]); + + MITK_INFO << "Reading Data..."; + auto input = ReadBothMatrix(trainingPath, predictionPath); + + MITK_INFO << "Calculating Weights..."; + mitk::GeneralizedLinearModel glm(input.feature, input.label); + auto weights = glm.ExpMu(input.feature); + + MITK_INFO << "Writing Weights ..."; + MITK_INFO << outputPath; + std::ofstream file(outputPath); + for (unsigned int i = 0; i < input.label.size(); ++i) + { + if (input.label(i) < 0.5) + { + file << (input.ratio * weights(i)) << std::endl; + } + } + file.close(); + + + ////////////////////////////////////////////////////////////////////////////// + // Read Images + ////////////////////////////////////////////////////////////////////////////// + //mitk::DataCollection::Pointer col = mitk::DataCollection::New(); + + //MITK_INFO << "Arg 2 " << argv[2]; + //mitk::Image::Pointer sur=mitk::IOUtil::LoadImage(argv[2]); + //col->AddData(sur.GetPointer(),"sur"); + //MITK_INFO << "Arg 3 " << argv[3]; + //mitk::Image::Pointer mask=mitk::IOUtil::LoadImage(argv[3]); + //col->AddData(mask.GetPointer(),"mask"); + + //std::vector modalities; + //for (int i = 4; i < argc; ++i) + //{ + // MITK_INFO << "Img " << argv[i]; + // std::stringstream ss; + // ss << i; + // modalities.push_back(ss.str()); + // mitk::Image::Pointer img = mitk::IOUtil::LoadImage(argv[i]); + // col->AddData(img.GetPointer(),ss.str()); + //} + + //mitk::LRDensityEstimation est; + //est.SetCollection(col); + //est.SetTrainMask("sur"); + //est.SetTestMask("mask"); + //est.SetModalities(modalities); + //est.SetWeightName("weight"); + //est.Update(); + + //mitk::Image::Pointer w= col->GetMitkImage("weight"); + //mitk::IOUtil::SaveImage(w,argv[1]); + + return 0; +} + +#endif \ No newline at end of file diff --git a/Modules/Classification/CLMiniApps/CMakeLists.txt b/Modules/Classification/CLMiniApps/CMakeLists.txt index b163d6dcb6..ab24948280 100644 --- a/Modules/Classification/CLMiniApps/CMakeLists.txt +++ b/Modules/Classification/CLMiniApps/CMakeLists.txt @@ -1,62 +1,143 @@ option(BUILD_ClassificationMiniApps "Build commandline tools for classification" OFF) if(BUILD_ClassificationMiniApps OR MITK_BUILD_ALL_APPS) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) # list of miniapps # if an app requires additional dependencies # they are added after a "^^" and separated by "_" set( classificationminiapps RandomForestTraining^^MitkCLVigraRandomForest NativeHeadCTSegmentation^^MitkCLVigraRandomForest ManualSegmentationEvaluation^^MitkCLVigraRandomForest - CLGlobalImageFeatures^^MitkCLUtilities + CLScreenshot^^MitkCore_MitkQtWidgetsExt_MitkCLUtilities + CLDicom2Nrrd^^MitkCore + CLImageTypeConverter^^MitkCore + CLResampleImageToReference^^MitkCore + CLGlobalImageFeatures^^MitkCLUtilities_MitkQtWidgetsExt CLMRNormalization^^MitkCLUtilities_MitkCLMRUtilities CLStaple^^MitkCLUtilities CLVoxelFeatures^^MitkCLUtilities - CLDicom2Nrrd^^ CLPolyToNrrd^^ + CLPlanarFigureToNrrd^^MitkCore_MitkSegmentation_MitkMultilabel CLSimpleVoxelClassification^^MitkDataCollection_MitkCLVigraRandomForest CLVoxelClassification^^MitkDataCollection_MitkCLImportanceWeighting_MitkCLVigraRandomForest CLBrainMask^^MitkCLUtilities + XRaxSimulationFromCT^^MitkCLUtilities + CLRandomSampling^^MitkCore_MitkCLUtilities + CLRemoveEmptyVoxels^^MitkCore + CLN4^^MitkCore + CLSkullMask^^MitkCore + CLPointSetToSegmentation^^ + CLMultiForestPrediction^^MitkDataCollection_MitkCLVigraRandomForest + CLNrrdToPoly^^MitkCore + CL2Dto3DImage^^MitkCore + CLWeighting^^MitkCore_MitkCLImportanceWeighting_MitkCLUtilities + CLOverlayRoiCenterOfMass^^MitkCore_MitkCLUtilities_MitkQtWidgetsExt + CLLungSegmentation^^MitkCore_MitkSegmentation_MitkMultilabel # RandomForestPrediction^^MitkCLVigraRandomForest ) foreach(classificationminiapps ${classificationminiapps}) # extract mini app name and dependencies string(REPLACE "^^" "\\;" miniapp_info ${classificationminiapps}) set(miniapp_info_list ${miniapp_info}) list(GET miniapp_info_list 0 appname) list(GET miniapp_info_list 1 raw_dependencies) string(REPLACE "_" "\\;" dependencies "${raw_dependencies}") set(dependencies_list ${dependencies}) - mitkFunctionCreateCommandLineApp( - NAME ${appname} - DEPENDS MitkCore MitkCLCore ${dependencies_list} - PACKAGE_DEPENDS Vigra Qt5|Core + mitk_create_executable(${appname} + DEPENDS MitkCore MitkCLCore MitkCommandLine ${dependencies_list} + PACKAGE_DEPENDS ITK Qt5|Core Vigra + CPP_FILES ${appname}.cpp ) + # CPP_FILES ${appname}.cpp mitkCommandLineParser.cpp + + if(EXECUTABLE_IS_ENABLED) + + # On Linux, create a shell script to start a relocatable application + if(UNIX AND NOT APPLE) + install(PROGRAMS "${MITK_SOURCE_DIR}/CMake/RunInstalledApp.sh" DESTINATION "." RENAME ${EXECUTABLE_TARGET}.sh) + endif() + + get_target_property(_is_bundle ${EXECUTABLE_TARGET} MACOSX_BUNDLE) + + if(APPLE) + if(_is_bundle) + set(_target_locations ${EXECUTABLE_TARGET}.app) + set(${_target_locations}_qt_plugins_install_dir ${EXECUTABLE_TARGET}.app/Contents/MacOS) + set(_bundle_dest_dir ${EXECUTABLE_TARGET}.app/Contents/MacOS) + set(_qt_plugins_for_current_bundle ${EXECUTABLE_TARGET}.app/Contents/MacOS) + set(_qt_conf_install_dirs ${EXECUTABLE_TARGET}.app/Contents/Resources) + install(TARGETS ${EXECUTABLE_TARGET} BUNDLE DESTINATION . ) + else() + if(NOT MACOSX_BUNDLE_NAMES) + set(_qt_conf_install_dirs bin) + set(_target_locations bin/${EXECUTABLE_TARGET}) + set(${_target_locations}_qt_plugins_install_dir bin) + install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION bin) + else() + foreach(bundle_name ${MACOSX_BUNDLE_NAMES}) + list(APPEND _qt_conf_install_dirs ${bundle_name}.app/Contents/Resources) + set(_current_target_location ${bundle_name}.app/Contents/MacOS/${EXECUTABLE_TARGET}) + list(APPEND _target_locations ${_current_target_location}) + set(${_current_target_location}_qt_plugins_install_dir ${bundle_name}.app/Contents/MacOS) + message( " set(${_current_target_location}_qt_plugins_install_dir ${bundle_name}.app/Contents/MacOS) ") + + install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION ${bundle_name}.app/Contents/MacOS/) + endforeach() + endif() + endif() + else() + set(_target_locations bin/${EXECUTABLE_TARGET}${CMAKE_EXECUTABLE_SUFFIX}) + set(${_target_locations}_qt_plugins_install_dir bin) + set(_qt_conf_install_dirs bin) + install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION bin) + endif() + endif() endforeach() - # This mini app does not depend on MitkCLCore at all - mitkFunctionCreateCommandLineApp( - NAME CLImageConverter - DEPENDS MitkCore ${dependencies_list} - ) + # This mini app does not depend on mitkDiffusionImaging at all - mitkFunctionCreateCommandLineApp( - NAME CLSurWeighting - DEPENDS MitkCore MitkCLUtilities MitkDataCollection MitkCLImportanceWeighting ${dependencies_list} + mitk_create_executable(CLMatchPointReg + DEPENDS MitkCore MitkCLUtilities MitkMatchPointRegistration MitkCommandLine MitkMatchPointRegistrationUI + PACKAGE_DEPENDS ITK Qt5|Core Vigra MatchPoint + CPP_FILES CLMatchPointReg.cpp ) + #mitk_create_executable(CLGlobalImageFeatures + # DEPENDS MitkCore MitkCLUtilities + # CPP_FILES CLGlobalImageFeatures.cpp mitkCommandLineParser.cpp + #) + #mitk_create_executable(CLBrainMask + # DEPENDS MitkCore MitkCLUtilities + # CPP_FILES CLBrainMask.cpp mitkCommandLineParser.cpp + #) + #mitk_create_executable(CLImageConverter + # DEPENDS MitkCore + # CPP_FILES CLImageConverter.cpp mitkCommandLineParser.cpp + #) + #mitk_create_executable(CLSurWeighting + # DEPENDS MitkCore MitkCLUtilities MitkDataCollection #MitkCLImportanceWeighting + # CPP_FILES CLSurWeighting.cpp mitkCommandLineParser.cpp + #) + #mitk_create_executable(CLImageCropper + # DEPENDS MitkCore MitkCLUtilities MitkAlgorithmsExt + # CPP_FILES CLImageCropper.cpp mitkCommandLineParser.cpp + #) - mitkFunctionCreateCommandLineApp( - NAME CLImageCropper - DEPENDS MitkCore MitkCLUtilities MitkAlgorithmsExt ${dependencies_list} - ) + # On Linux, create a shell script to start a relocatable application + if(UNIX AND NOT APPLE) + install(PROGRAMS "${MITK_SOURCE_DIR}/CMake/RunInstalledApp.sh" DESTINATION "." RENAME ${EXECUTABLE_TARGET}.sh) + endif() + + if(EXECUTABLE_IS_ENABLED) + MITK_INSTALL_TARGETS(EXECUTABLES ${EXECUTABLE_TARGET}) + endif() endif() diff --git a/Modules/Classification/CLMiniApps/XRaxSimulationFromCT.cpp b/Modules/Classification/CLMiniApps/XRaxSimulationFromCT.cpp new file mode 100644 index 0000000000..25e7386938 --- /dev/null +++ b/Modules/Classification/CLMiniApps/XRaxSimulationFromCT.cpp @@ -0,0 +1,227 @@ +/*=================================================================== + +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 "mitkDicomSeriesReader.h" +#include "mitkProperties.h" + +#include "mitkCommandLineParser.h" +#include "mitkIOUtil.h" + +#include "itkImageRegionIterator.h" +// MITK +#include +#include +#include + +struct Params { + bool invert; + float zeroValue; +}; + +template +void +CreateXRay(itk::Image* itkImage, mitk::Image::Pointer mask1, std::string output, Params param) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskType; + typedef itk::Image NewImageType; + + typename MaskType::Pointer itkMask = MaskType::New(); + mitk::CastToItkImage(mask1, itkMask); + + NewImageType::SpacingType newSpacing; + auto spacing = itkImage->GetSpacing(); + spacing[0] = itkImage->GetSpacing()[0]; + spacing[1] = itkImage->GetSpacing()[1]; + spacing[2] = itkImage->GetSpacing()[2]; + spacing = itkImage->GetSpacing(); + + NewImageType::RegionType region1,region2,region3,region1m,region2m,region3m; + NewImageType::IndexType start; + start[0] = 0; start[1] = 0; + + NewImageType::SizeType size1, size2, size3; + size1[0] = mask1->GetDimensions()[0]; + size2[0] = mask1->GetDimensions()[0]; + size3[0] = mask1->GetDimensions()[1]; + size1[1] = mask1->GetDimensions()[1]; + size2[1] = mask1->GetDimensions()[2]; + size3[1] = mask1->GetDimensions()[2]; + + region1.SetSize(size1); + region1m.SetSize(size1); + region2.SetSize(size2); + region2m.SetSize(size2); + region3.SetSize(size3); + region3m.SetSize(size3); + region1.SetIndex(start); + region1m.SetIndex(start); + region2.SetIndex(start); + region2m.SetIndex(start); + region3.SetIndex(start); + region3m.SetIndex(start); + + NewImageType::Pointer image1 = NewImageType::New(); + image1->SetRegions(region1); + image1->Allocate(); + image1->FillBuffer(0); + newSpacing[0] = spacing[0]; newSpacing[1] = spacing[1]; + image1->SetSpacing(newSpacing); + NewImageType::Pointer image2 = NewImageType::New(); + image2->SetRegions(region2); + image2->Allocate(); + image2->FillBuffer(0); + newSpacing[0] = spacing[0]; newSpacing[1] = spacing[2]; + image2->SetSpacing(newSpacing); + NewImageType::Pointer image3 = NewImageType::New(); + image3->SetRegions(region3); + image3->Allocate(); + image3->FillBuffer(0); + newSpacing[0] = spacing[1]; newSpacing[1] = spacing[2]; + image3->SetSpacing(newSpacing); + NewImageType::Pointer image1m = NewImageType::New(); + image1m->SetRegions(region1m); + image1m->Allocate(); + image1m->FillBuffer(0); + newSpacing[0] = spacing[0]; newSpacing[1] = spacing[1]; + image1m->SetSpacing(newSpacing); + NewImageType::Pointer image2m = NewImageType::New(); + image2m->SetRegions(region2m); + image2m->Allocate(); + image2m->FillBuffer(0); + newSpacing[0] = spacing[0]; newSpacing[1] = spacing[2]; + image2m->SetSpacing(newSpacing); + NewImageType::Pointer image3m = NewImageType::New(); + image3m->SetRegions(region3m); + image3m->Allocate(); + image3m->FillBuffer(0); + newSpacing[0] = spacing[1]; newSpacing[1] = spacing[2]; + image3m->SetSpacing(newSpacing); + + for (unsigned int x = 0; x < mask1->GetDimensions()[0]; ++x) + { + for (unsigned int y = 0; y < mask1->GetDimensions()[1]; ++y) + { + for (unsigned int z = 0; z < mask1->GetDimensions()[2]; ++z) + { + NewImageType::IndexType newIndex; + typename ImageType::IndexType index; + index[0] = x; index[1] = y; index[2] = z; + double pixel = itkImage->GetPixel(index)+1024; + pixel = pixel / 1000.0; + pixel = (pixel < 0)? 0 : pixel; + newIndex[0] = x; newIndex[1] = y; + image1->SetPixel(newIndex, image1->GetPixel(newIndex) + pixel); + newIndex[0] = x; newIndex[1] = z; + image2->SetPixel(newIndex, image2->GetPixel(newIndex) + pixel); + newIndex[0] = y; newIndex[1] = z; + image3->SetPixel(newIndex, image3->GetPixel(newIndex) + pixel); + if (itkMask->GetPixel(index) > 0 && !param.invert) + { + pixel = param.zeroValue + 1024; + pixel = pixel / 1000.0; + } + if (itkMask->GetPixel(index) < 1 && param.invert) + { + pixel = param.zeroValue + 1024; + pixel = pixel / 1000.0; + } + pixel = (pixel < 0)? 0 : pixel; + newIndex[0] = x; newIndex[1] = y; + image1m->SetPixel(newIndex, image1m->GetPixel(newIndex) + pixel); + newIndex[0] = x; newIndex[1] = z; + image2m->SetPixel(newIndex, image2m->GetPixel(newIndex) + pixel); + newIndex[0] = y; newIndex[1] = z; + image3m->SetPixel(newIndex, image3m->GetPixel(newIndex) + pixel); + } + } + } + + + mitk::Image::Pointer img = mitk::ImportItkImage(image1); + mitk::IOUtil::Save(img, output + "1.nrrd"); + img = mitk::ImportItkImage(image2); + mitk::IOUtil::Save(img, output + "2.nrrd"); + img = mitk::ImportItkImage(image3); + mitk::IOUtil::Save(img, output + "3.nrrd"); + img = mitk::ImportItkImage(image1m); + mitk::IOUtil::Save(img, output + "1m.nrrd"); + img = mitk::ImportItkImage(image2m); + mitk::IOUtil::Save(img, output + "2m.nrrd"); + img = mitk::ImportItkImage(image3m); + mitk::IOUtil::Save(img, output + "3m.nrrd"); +} + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + + parser.setTitle("Dicom Loader"); + parser.setCategory("Preprocessing Tools"); + parser.setDescription(""); + parser.setContributor("MBI"); + + parser.setArgumentPrefix("-","-"); + // Add command line argument names + parser.addArgument("help", "h",mitkCommandLineParser::Bool, "Help:", "Show this help text"); + parser.addArgument("input", "i", mitkCommandLineParser::InputDirectory, "Input image:", "Input folder", us::Any(), false); + parser.addArgument("mask", "m", mitkCommandLineParser::InputDirectory, "Input mask:", "Input folder", us::Any(), false); + parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output file:", "Output file", us::Any(), false); + parser.addArgument("invert", "invert", mitkCommandLineParser::Bool, "Input mask:", "Input folder", us::Any()); + parser.addArgument("zero_value", "zero", mitkCommandLineParser::Float, "Output file:", "Output file", us::Any()); + + std::map parsedArgs = parser.parseArguments(argc, argv); + + if (parsedArgs.size()==0) + return EXIT_FAILURE; + + // Show a help message + if ( parsedArgs.count("help") || parsedArgs.count("h")) + { + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + + std::string inputImage = us::any_cast(parsedArgs["input"]); + MITK_INFO << inputImage; + std::string inputMask = us::any_cast(parsedArgs["mask"]); + MITK_INFO << inputMask; + + Params param; + param.invert = false; + param.zeroValue = 0; + if (parsedArgs.count("invert")) + { + param.invert = true; + } + if (parsedArgs.count("zero_value")) + { + param.zeroValue = us::any_cast(parsedArgs["zero_value"]); + } + + + mitk::Image::Pointer image = mitk::IOUtil::LoadImage(inputImage); + mitk::Image::Pointer mask = mitk::IOUtil::LoadImage(inputMask); + + AccessByItk_3(image, CreateXRay, mask, parsedArgs["output"].ToString(),param); + + //const mitk::Image::Pointer image = *imageIter; + //mitk::IOUtil::SaveImage(image,outFileName); + + + + return EXIT_SUCCESS; +} diff --git a/Modules/Classification/CLUtilities/CMakeLists.txt b/Modules/Classification/CLUtilities/CMakeLists.txt index ba273c3c74..ebe7afa43e 100644 --- a/Modules/Classification/CLUtilities/CMakeLists.txt +++ b/Modules/Classification/CLUtilities/CMakeLists.txt @@ -1,6 +1,6 @@ MITK_CREATE_MODULE( - DEPENDS MitkCore MitkCLCore + DEPENDS MitkCore MitkCLCore MitkCommandLine PACKAGE_DEPENDS PUBLIC Eigen ) add_subdirectory(test) diff --git a/Modules/Classification/CLUtilities/files.cmake b/Modules/Classification/CLUtilities/files.cmake index cfcdfcfe53..17b04b392f 100644 --- a/Modules/Classification/CLUtilities/files.cmake +++ b/Modules/Classification/CLUtilities/files.cmake @@ -1,24 +1,38 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES + mitkCLResultWritter.cpp + Algorithms/itkLabelSampler.cpp Algorithms/itkSmoothedClassProbabilites.cpp + Algorithms/mitkRandomImageSampler.cpp Features/itkNeighborhoodFunctorImageFilter.cpp Features/itkLineHistogramBasedMassImageFilter.cpp GlobalImageFeatures/mitkGIFCooccurenceMatrix.cpp - GlobalImageFeatures/mitkGIFGrayLevelRunLength.cpp + GlobalImageFeatures/mitkGIFCooccurenceMatrix2.cpp + GlobalImageFeatures/mitkGIFGreyLevelRunLength.cpp + GlobalImageFeatures/mitkGIFImageDescriptionFeatures.cpp GlobalImageFeatures/mitkGIFFirstOrderStatistics.cpp + GlobalImageFeatures/mitkGIFFirstOrderHistogramStatistics.cpp GlobalImageFeatures/mitkGIFVolumetricStatistics.cpp - #GlobalImageFeatures/itkEnhancedScalarImageToRunLengthFeaturesFilter.hxx - #GlobalImageFeatures/itkEnhancedScalarImageToRunLengthMatrixFilter.hxx - #GlobalImageFeatures/itkEnhancedHistogramToRunLengthFeaturesFilter.hxx - #GlobalImageFeatures/itkEnhancedHistogramToTextureFeaturesFilter.hxx - #GlobalImageFeatures/itkEnhancedScalarImageToTextureFeaturesFilter.hxx + GlobalImageFeatures/mitkGIFNeighbouringGreyLevelDependenceFeatures.cpp + GlobalImageFeatures/mitkGIFNeighbourhoodGreyLevelDifference.cpp + GlobalImageFeatures/mitkGIFGreyLevelSizeZone.cpp + GlobalImageFeatures/mitkGIFGreyLevelDistanceZone.cpp + GlobalImageFeatures/mitkGIFLocalIntensity.cpp + GlobalImageFeatures/mitkGIFVolumetricDensityStatistics.cpp + GlobalImageFeatures/mitkGIFIntensityVolumeHistogramFeatures.cpp + GlobalImageFeatures/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.cpp + GlobalImageFeatures/mitkGIFCurvatureStatistic.cpp + + MiniAppUtils/mitkGlobalImageFeaturesParameter.cpp + MiniAppUtils/mitkSplitParameterToVector.cpp + mitkCLUtil.cpp ) set( TOOL_FILES ) diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToRunLengthFeaturesFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter.h similarity index 50% copy from Modules/Classification/CLUtilities/include/itkEnhancedHistogramToRunLengthFeaturesFilter.h copy to Modules/Classification/CLUtilities/include/itkEnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter.h index 2b2cbf07db..c3952de08c 100644 --- a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToRunLengthFeaturesFilter.h +++ b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter.h @@ -1,216 +1,188 @@ /*=================================================================== 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. ===================================================================*/ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ -#ifndef __itkEnhancedHistogramToRunLengthFeaturesFilter_h -#define __itkEnhancedHistogramToRunLengthFeaturesFilter_h +#ifndef __itkEnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter_h +#define __itkEnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter_h #include "itkHistogram.h" #include "itkMacro.h" #include "itkProcessObject.h" #include "itkSimpleDataObjectDecorator.h" namespace itk { namespace Statistics { - /** \class EnhancedHistogramToRunLengthFeaturesFilter + /** \class EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter * \brief This class computes texture feature coefficients from a grey level - * run-length matrix. + * Zone-length matrix. * - * By default, run length features are computed for each spatial + * By default, Zone length features are computed for each spatial * direction and then averaged afterward, so it is possible to access the * standard deviations of the texture features. These values give a clue as * to texture anisotropy. However, doing this is much more work, because it * involved computing one for each offset given. To compute a single matrix * using the first offset, call FastCalculationsOn(). If this is called, * then the texture standard deviations will not be computed (and will be set * to zero), but texture computation will be much faster. * * This class is templated over the input histogram type. * * Print references: - * M. M. Galloway. Texture analysis using gray level run lengths. Computer + * M. M. Galloway. Texture analysis using gray level Zone lengths. Computer * Graphics and Image Processing, 4:172-179, 1975. * * A. Chu, C. M. Sehgal, and J. F. Greenleaf. Use of gray value distribution of - * run lengths for texture analysis. Pattern Recognition Letters, 11:415-420, + * Zone lengths for texture analysis. Pattern Recognition Letters, 11:415-420, * 1990. * * B. R. Dasarathy and E. B. Holder. Image characterizations based on joint - * gray-level run-length distributions. Pattern Recognition Letters, 12:490-502, + * gray-level Zone-length distributions. Pattern Recognition Letters, 12:490-502, * 1991. * * IJ article: http://hdl.handle.net/1926/1374 * - * \sa ScalarImageToRunLengthFeaturesFilter - * \sa ScalarImageToRunLengthMatrixFilter - * \sa EnhancedHistogramToRunLengthFeaturesFilter + * \sa ScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter + * \sa ScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter + * \sa EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter * * \author: Nick Tustison * \ingroup ITKStatistics */ template< typename THistogram > - class EnhancedHistogramToRunLengthFeaturesFilter : public ProcessObject + class EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter : public ProcessObject { public: /** Standard typedefs */ - typedef EnhancedHistogramToRunLengthFeaturesFilter Self; + typedef EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter Self; typedef ProcessObject Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; - /** Run-time type information (and related methods). */ - itkTypeMacro( EnhancedHistogramToRunLengthFeaturesFilter, ProcessObject ); + /** Zone-time type information (and related methods). */ + itkTypeMacro( EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter, ProcessObject ); /** standard New() method support */ itkNewMacro( Self ); typedef THistogram HistogramType; typedef typename HistogramType::Pointer HistogramPointer; typedef typename HistogramType::ConstPointer HistogramConstPointer; typedef typename HistogramType::MeasurementType MeasurementType; typedef typename HistogramType::MeasurementVectorType MeasurementVectorType; typedef typename HistogramType::IndexType IndexType; typedef typename HistogramType:: TotalAbsoluteFrequencyType FrequencyType; /** Method to Set/Get the input Histogram */ using Superclass::SetInput; - void SetInput ( const HistogramType * histogram ); + void SetInput (const HistogramType * histogram ); const HistogramType * GetInput() const; /** Smart Pointer type to a DataObject. */ typedef DataObject::Pointer DataObjectPointer; /** Type of DataObjects used for scalar outputs */ typedef SimpleDataObjectDecorator MeasurementObjectType; - /** Methods to return the short run emphasis. */ - MeasurementType GetShortRunEmphasis() const; - const MeasurementObjectType* GetShortRunEmphasisOutput() const; + /** Methods to return the short Zone emphasis. */ + MeasurementType GetCoarseness() const; + const MeasurementObjectType* GetCoarsenessOutput() const; - /** Methods to return the long run emphasis. */ - MeasurementType GetLongRunEmphasis() const; - const MeasurementObjectType* GetLongRunEmphasisOutput() const; + /** Methods to return the long Zone emphasis. */ + MeasurementType GetContrast() const; + const MeasurementObjectType* GetContrastOutput() const; /** Methods to return the grey level nonuniformity. */ - MeasurementType GetGreyLevelNonuniformity() const; - const MeasurementObjectType* GetGreyLevelNonuniformityOutput() const; + MeasurementType GetBusyness() const; + const MeasurementObjectType* GetBusynessOutput() const; - /** Methods to return the run length nonuniformity. */ - MeasurementType GetRunLengthNonuniformity() const; - const MeasurementObjectType* GetRunLengthNonuniformityOutput() const; + /** Methods to return the Zone length nonuniformity. */ + MeasurementType GetComplexity() const; + const MeasurementObjectType* GetComplexityOutput() const; - /** Methods to return the low grey level run emphasis. */ - MeasurementType GetLowGreyLevelRunEmphasis() const; - const MeasurementObjectType* GetLowGreyLevelRunEmphasisOutput() const; + /** Methods to return the low grey level Zone emphasis. */ + MeasurementType GetStrength() const; + const MeasurementObjectType* GetStrengthOutput() const; - /** Methods to return the high grey level run emphasis. */ - MeasurementType GetHighGreyLevelRunEmphasis() const; - const MeasurementObjectType* GetHighGreyLevelRunEmphasisOutput() const; - - /** Methods to return the short run low grey level run emphasis. */ - MeasurementType GetShortRunLowGreyLevelEmphasis() const; - const MeasurementObjectType* GetShortRunLowGreyLevelEmphasisOutput() const; - - /** Methods to return the short run high grey level run emphasis. */ - MeasurementType GetShortRunHighGreyLevelEmphasis() const; - const MeasurementObjectType* GetShortRunHighGreyLevelEmphasisOutput() const; - - /** Methods to return the long run low grey level run emphasis. */ - MeasurementType GetLongRunLowGreyLevelEmphasis() const; - const MeasurementObjectType* GetLongRunLowGreyLevelEmphasisOutput() const; - - /** Methods to return the long run high grey level run emphasis. */ - MeasurementType GetLongRunHighGreyLevelEmphasis() const; - const MeasurementObjectType* GetLongRunHighGreyLevelEmphasisOutput() const; - - /** Methods to return the long run high grey level run emphasis. */ - MeasurementType GetRunPercentage() const; - const MeasurementObjectType* GetRunPercentageOutput() const; - - /** Methods to return the long run high grey level run emphasis. */ - MeasurementType GetNumberOfRuns() const; - const MeasurementObjectType* GetNumberOfRunsOutput() const; - - itkGetMacro( TotalNumberOfRuns, unsigned long ); + itkGetMacro( TotalNumberOfValidVoxels, unsigned long ); itkGetConstMacro(NumberOfVoxels, unsigned long); itkSetMacro(NumberOfVoxels, unsigned long); - /** Run-length feature types */ + void SetSiMatrix(double* matrix) + { + m_siMatrix = matrix; + } + + /** Zone-length feature types */ typedef enum { - ShortRunEmphasis, - LongRunEmphasis, - GreyLevelNonuniformity, - RunLengthNonuniformity, - LowGreyLevelRunEmphasis, - HighGreyLevelRunEmphasis, - ShortRunLowGreyLevelEmphasis, - ShortRunHighGreyLevelEmphasis, - LongRunLowGreyLevelEmphasis, - LongRunHighGreyLevelEmphasis, - RunPercentage, - NumberOfRuns - } RunLengthFeatureName; - - /** convenience method to access the run length values */ - MeasurementType GetFeature( RunLengthFeatureName name ); + Coarseness, + Contrast, + Busyness, + Complexity, + Strength + } NeighbourhoodGreyLevelDifferenceFeatureName; + + /** convenience method to access the Zone length values */ + MeasurementType GetFeature( NeighbourhoodGreyLevelDifferenceFeatureName name ); protected: - EnhancedHistogramToRunLengthFeaturesFilter(); - ~EnhancedHistogramToRunLengthFeaturesFilter() override {}; - void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE; + EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter(); + ~EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter() {}; + virtual void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE; /** Make a DataObject to be used for output output. */ typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType; using Superclass::MakeOutput; - DataObjectPointer MakeOutput( DataObjectPointerArraySizeType ) ITK_OVERRIDE; + virtual DataObjectPointer MakeOutput( DataObjectPointerArraySizeType ) ITK_OVERRIDE; - void GenerateData() ITK_OVERRIDE; + virtual void GenerateData() ITK_OVERRIDE; private: - EnhancedHistogramToRunLengthFeaturesFilter(const Self&); //purposely not implemented + EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented - unsigned long m_TotalNumberOfRuns; + unsigned long m_TotalNumberOfValidVoxels; unsigned long m_NumberOfVoxels; + + double* m_siMatrix; }; } // end of namespace Statistics } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION -#include "itkEnhancedHistogramToRunLengthFeaturesFilter.hxx" +#include "itkEnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter.hxx" #endif #endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter.hxx b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter.hxx new file mode 100644 index 0000000000..a60da12f62 --- /dev/null +++ b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter.hxx @@ -0,0 +1,330 @@ +/*=================================================================== + +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. + +===================================================================*/ + +/*========================================================================= +* +* Copyright Insight Software Consortium +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0.txt +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*=========================================================================*/ +#ifndef __itkEnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter_hxx +#define __itkEnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter_hxx + +#include "itkEnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter.h" + +#include "itkNumericTraits.h" +#include "vnl/vnl_math.h" + +namespace itk { +namespace Statistics { +//constructor +template +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter +::EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter() : + m_NumberOfVoxels(1) +{ + this->ProcessObject::SetNumberOfRequiredInputs( 1 ); + + // allocate the data objects for the outputs which are + // just decorators real types + for( unsigned int i = 0; i < 5; i++ ) + { + this->ProcessObject::SetNthOutput( i, this->MakeOutput( i ) ); + } +} + +template +void +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter< THistogram> +::SetInput(const HistogramType *histogram ) +{ + this->ProcessObject::SetNthInput( 0, const_cast( histogram ) ); +} + + +template +const typename +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter::HistogramType * +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter< THistogram> +::GetInput() const +{ + if ( this->GetNumberOfInputs() < 1 ) + { + return ITK_NULLPTR; + } + return itkDynamicCastInDebugMode(this->ProcessObject::GetInput(0) ); +} + +template +typename +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter::DataObjectPointer +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter +::MakeOutput( DataObjectPointerArraySizeType itkNotUsed( idx ) ) +{ + return MeasurementObjectType::New().GetPointer(); +} + +template +void +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter< THistogram>:: +GenerateData( void ) +{ + const HistogramType * inputHistogram = this->GetInput(); //The nis + + this->m_TotalNumberOfValidVoxels = static_cast + ( inputHistogram->GetTotalFrequency() ); + + MeasurementType coarseness = NumericTraits::ZeroValue(); + MeasurementType contrast = NumericTraits::ZeroValue(); + MeasurementType busyness = NumericTraits::ZeroValue(); + MeasurementType complexity = NumericTraits::ZeroValue(); + MeasurementType strength = NumericTraits::ZeroValue(); + + typedef typename HistogramType::ConstIterator HistogramIterator; + + long unsigned int Ng = inputHistogram->GetSize(0); + std::cout << "No of bins: " << Ng << " total number of valid voxels: " << this->m_TotalNumberOfValidVoxels << std::endl; + + double sumPiSi = 0.0; + double sumSi = 0.0; + unsigned int Ngp = 0; + unsigned int Nv = 0; + + //Calculate sum(pi*si). + for ( HistogramIterator hit = inputHistogram->Begin(); + hit != inputHistogram->End(); ++hit ) + { + IndexType index = hit.GetIndex(); + + MeasurementType ni = hit.GetFrequency(); + double si = m_siMatrix[index[0]]; + if ( ni == 0 ) + { + continue; + } + sumPiSi += ni*si; + sumSi += si; + Ngp++; + Nv += ni; + } + + sumPiSi /= (Nv > 0) ? Nv : 1; + + + //Calculate the other features + //pi = ni / Nv, so we can simply calculate with ni instead of pi + + double pipj = 0.0; + double ipijpj = 0.0; + double complexitySum = 0.0; + double strengthSum = 0.0; + + for ( HistogramIterator hit = inputHistogram->Begin(); + hit != inputHistogram->End(); ++hit ) + { + MeasurementVectorType measurement = hit.GetMeasurementVector(); + IndexType index = hit.GetIndex(); + //MITK_WARN << "current index " << index; + + MeasurementType ni = hit.GetFrequency(); + double si = m_siMatrix[index[0]]; + + double i = measurement[0]; + + for ( HistogramIterator hit2 = inputHistogram->Begin(); + hit2 != inputHistogram->End(); ++hit2 ) + { + MeasurementVectorType measurement2 = hit2.GetMeasurementVector(); + IndexType index2 = hit2.GetIndex(); + + MeasurementType nj= hit2.GetFrequency(); + double sj = m_siMatrix[index2[0]]; + + double j = measurement2[0]; + + pipj += ni*nj*(i-j)*(i-j); + ipijpj += std::abs(i*ni - j*nj); + + if(ni != 0 && nj != 0) + complexitySum += std::abs(i-j) * (ni*si + nj*sj)/(ni+nj); + + strengthSum += (ni+nj)*(i-j)*(i-j); + } + } + + + //Calculate Coarseness + coarseness = (sumPiSi == 0) ? 1e6 : 1.0 / sumPiSi; + contrast = (Ngp <= 1 || Nv == 0) ? 0 : (1.0/(Ngp * (Ngp - 1))*pipj / Nv / Nv) * (sumSi / Nv / Nv); + busyness = (Ngp <= 1 || ipijpj == 0) ? 0 : sumPiSi / ipijpj / Nv; + complexity = (Nv == 0) ? 0: complexitySum / Nv / Nv; + strength = (sumSi == 0 || Nv == 0) ? 0 : strengthSum / Nv / sumSi; + + //Calculate the other features. + + MeasurementObjectType* CoarsenessOutputObject = + static_cast( this->ProcessObject::GetOutput( 0 ) ); + CoarsenessOutputObject->Set( coarseness ); + + MeasurementObjectType* ContrastOutputObject = + static_cast( this->ProcessObject::GetOutput( 1 ) ); + ContrastOutputObject->Set( contrast ); + + MeasurementObjectType* BusynessOutputObject = + static_cast( this->ProcessObject::GetOutput( 2 ) ); + BusynessOutputObject->Set( busyness ); + + MeasurementObjectType* ComplexityOutputObject = + static_cast( this->ProcessObject::GetOutput( 3 ) ); + ComplexityOutputObject->Set( complexity ); + + MeasurementObjectType* StrengthOutputObject = + static_cast( this->ProcessObject::GetOutput( 4 ) ); + StrengthOutputObject->Set( strength ); + +} + +template +const +typename EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter::MeasurementObjectType* +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter +::GetCoarsenessOutput() const +{ + return itkDynamicCastInDebugMode(this->ProcessObject::GetOutput( 0 ) ); +} + +template +const +typename EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter::MeasurementObjectType* +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter +::GetContrastOutput() const +{ + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 1 ) ); +} + +template +const +typename EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter::MeasurementObjectType* +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter +::GetBusynessOutput() const +{ + return itkDynamicCastInDebugMode(this->ProcessObject::GetOutput( 2 ) ); +} + +template +const +typename EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter::MeasurementObjectType* +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter +::GetComplexityOutput() const +{ + return itkDynamicCastInDebugMode(this->ProcessObject::GetOutput( 3 ) ); +} + +template +const +typename EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter::MeasurementObjectType* +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter +::GetStrengthOutput() const +{ + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 4 ) ); +} + + +template +typename EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter::MeasurementType +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter +::GetCoarseness() const +{ + return this->GetCoarsenessOutput()->Get(); +} + +template +typename EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter::MeasurementType +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter +::GetContrast() const +{ + return this->GetContrastOutput()->Get(); +} + +template +typename EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter::MeasurementType +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter +::GetBusyness() const +{ + return this->GetBusynessOutput()->Get(); +} + +template +typename EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter::MeasurementType +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter +::GetComplexity() const +{ + return this->GetComplexityOutput()->Get(); +} + +template +typename EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter::MeasurementType +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter +::GetStrength() const +{ + return this->GetStrengthOutput()->Get(); +} + + +template +typename EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter< THistogram>::MeasurementType +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter +::GetFeature( NeighbourhoodGreyLevelDifferenceFeatureName feature ) +{ + switch( feature ) + { + case Coarseness: + return this->GetCoarseness(); + case Contrast: + return this->GetContrast(); + case Busyness: + return this->GetBusyness(); + case Complexity: + return this->GetComplexity(); + case Strength: + return this->GetStrength(); + default: + return 0; + } +} + +template< typename THistogram> +void +EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter< THistogram>:: +PrintSelf(std::ostream& os, Indent indent) const +{ + Superclass::PrintSelf( os,indent ); +} +} // end of namespace Statistics +} // end of namespace itk + +#endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToRunLengthFeaturesFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToRunLengthFeaturesFilter.h index 2b2cbf07db..7bc4856883 100644 --- a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToRunLengthFeaturesFilter.h +++ b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToRunLengthFeaturesFilter.h @@ -1,216 +1,241 @@ /*=================================================================== 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. ===================================================================*/ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef __itkEnhancedHistogramToRunLengthFeaturesFilter_h #define __itkEnhancedHistogramToRunLengthFeaturesFilter_h #include "itkHistogram.h" #include "itkMacro.h" #include "itkProcessObject.h" #include "itkSimpleDataObjectDecorator.h" namespace itk { namespace Statistics { /** \class EnhancedHistogramToRunLengthFeaturesFilter * \brief This class computes texture feature coefficients from a grey level * run-length matrix. * * By default, run length features are computed for each spatial * direction and then averaged afterward, so it is possible to access the * standard deviations of the texture features. These values give a clue as * to texture anisotropy. However, doing this is much more work, because it * involved computing one for each offset given. To compute a single matrix * using the first offset, call FastCalculationsOn(). If this is called, * then the texture standard deviations will not be computed (and will be set * to zero), but texture computation will be much faster. * * This class is templated over the input histogram type. * * Print references: * M. M. Galloway. Texture analysis using gray level run lengths. Computer * Graphics and Image Processing, 4:172-179, 1975. * * A. Chu, C. M. Sehgal, and J. F. Greenleaf. Use of gray value distribution of * run lengths for texture analysis. Pattern Recognition Letters, 11:415-420, * 1990. * * B. R. Dasarathy and E. B. Holder. Image characterizations based on joint * gray-level run-length distributions. Pattern Recognition Letters, 12:490-502, * 1991. * * IJ article: http://hdl.handle.net/1926/1374 * * \sa ScalarImageToRunLengthFeaturesFilter * \sa ScalarImageToRunLengthMatrixFilter * \sa EnhancedHistogramToRunLengthFeaturesFilter * * \author: Nick Tustison * \ingroup ITKStatistics */ template< typename THistogram > class EnhancedHistogramToRunLengthFeaturesFilter : public ProcessObject { public: /** Standard typedefs */ typedef EnhancedHistogramToRunLengthFeaturesFilter Self; typedef ProcessObject Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro( EnhancedHistogramToRunLengthFeaturesFilter, ProcessObject ); /** standard New() method support */ itkNewMacro( Self ); typedef THistogram HistogramType; typedef typename HistogramType::Pointer HistogramPointer; typedef typename HistogramType::ConstPointer HistogramConstPointer; typedef typename HistogramType::MeasurementType MeasurementType; typedef typename HistogramType::MeasurementVectorType MeasurementVectorType; typedef typename HistogramType::IndexType IndexType; typedef typename HistogramType:: TotalAbsoluteFrequencyType FrequencyType; /** Method to Set/Get the input Histogram */ using Superclass::SetInput; void SetInput ( const HistogramType * histogram ); const HistogramType * GetInput() const; /** Smart Pointer type to a DataObject. */ typedef DataObject::Pointer DataObjectPointer; /** Type of DataObjects used for scalar outputs */ typedef SimpleDataObjectDecorator MeasurementObjectType; /** Methods to return the short run emphasis. */ MeasurementType GetShortRunEmphasis() const; const MeasurementObjectType* GetShortRunEmphasisOutput() const; /** Methods to return the long run emphasis. */ MeasurementType GetLongRunEmphasis() const; const MeasurementObjectType* GetLongRunEmphasisOutput() const; /** Methods to return the grey level nonuniformity. */ MeasurementType GetGreyLevelNonuniformity() const; const MeasurementObjectType* GetGreyLevelNonuniformityOutput() const; + /** Methods to return the grey level nonuniformity. */ + MeasurementType GetGreyLevelNonuniformityNormalized() const; + const MeasurementObjectType* GetGreyLevelNonuniformityNormalizedOutput() const; + /** Methods to return the run length nonuniformity. */ MeasurementType GetRunLengthNonuniformity() const; const MeasurementObjectType* GetRunLengthNonuniformityOutput() const; + /** Methods to return the run length nonuniformity. */ + MeasurementType GetRunLengthNonuniformityNormalized() const; + const MeasurementObjectType* GetRunLengthNonuniformityNormalizedOutput() const; + /** Methods to return the low grey level run emphasis. */ MeasurementType GetLowGreyLevelRunEmphasis() const; const MeasurementObjectType* GetLowGreyLevelRunEmphasisOutput() const; /** Methods to return the high grey level run emphasis. */ MeasurementType GetHighGreyLevelRunEmphasis() const; const MeasurementObjectType* GetHighGreyLevelRunEmphasisOutput() const; /** Methods to return the short run low grey level run emphasis. */ MeasurementType GetShortRunLowGreyLevelEmphasis() const; const MeasurementObjectType* GetShortRunLowGreyLevelEmphasisOutput() const; /** Methods to return the short run high grey level run emphasis. */ MeasurementType GetShortRunHighGreyLevelEmphasis() const; const MeasurementObjectType* GetShortRunHighGreyLevelEmphasisOutput() const; /** Methods to return the long run low grey level run emphasis. */ MeasurementType GetLongRunLowGreyLevelEmphasis() const; const MeasurementObjectType* GetLongRunLowGreyLevelEmphasisOutput() const; /** Methods to return the long run high grey level run emphasis. */ MeasurementType GetLongRunHighGreyLevelEmphasis() const; const MeasurementObjectType* GetLongRunHighGreyLevelEmphasisOutput() const; /** Methods to return the long run high grey level run emphasis. */ MeasurementType GetRunPercentage() const; const MeasurementObjectType* GetRunPercentageOutput() const; /** Methods to return the long run high grey level run emphasis. */ MeasurementType GetNumberOfRuns() const; const MeasurementObjectType* GetNumberOfRunsOutput() const; + /** Methods to return the grey level variance. */ + MeasurementType GetGreyLevelVariance() const; + const MeasurementObjectType* GetGreyLevelVarianceOutput() const; + + /** Methods to return the run length variance. */ + MeasurementType GetRunLengthVariance() const; + const MeasurementObjectType* GetRunLengthVarianceOutput() const; + + /** Methods to return the run entropy. */ + MeasurementType GetRunEntropy() const; + const MeasurementObjectType* GetRunEntropyOutput() const; + itkGetMacro( TotalNumberOfRuns, unsigned long ); itkGetConstMacro(NumberOfVoxels, unsigned long); itkSetMacro(NumberOfVoxels, unsigned long); /** Run-length feature types */ typedef enum { ShortRunEmphasis, LongRunEmphasis, GreyLevelNonuniformity, + GreyLevelNonuniformityNormalized, RunLengthNonuniformity, + RunLengthNonuniformityNormalized, LowGreyLevelRunEmphasis, HighGreyLevelRunEmphasis, ShortRunLowGreyLevelEmphasis, ShortRunHighGreyLevelEmphasis, LongRunLowGreyLevelEmphasis, LongRunHighGreyLevelEmphasis, RunPercentage, - NumberOfRuns + NumberOfRuns, + GreyLevelVariance, + RunLengthVariance, + RunEntropy } RunLengthFeatureName; /** convenience method to access the run length values */ MeasurementType GetFeature( RunLengthFeatureName name ); protected: EnhancedHistogramToRunLengthFeaturesFilter(); ~EnhancedHistogramToRunLengthFeaturesFilter() override {}; void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE; /** Make a DataObject to be used for output output. */ typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType; using Superclass::MakeOutput; DataObjectPointer MakeOutput( DataObjectPointerArraySizeType ) ITK_OVERRIDE; void GenerateData() ITK_OVERRIDE; private: EnhancedHistogramToRunLengthFeaturesFilter(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented unsigned long m_TotalNumberOfRuns; unsigned long m_NumberOfVoxels; }; } // end of namespace Statistics } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkEnhancedHistogramToRunLengthFeaturesFilter.hxx" #endif #endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToRunLengthFeaturesFilter.hxx b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToRunLengthFeaturesFilter.hxx index 39c7acf636..f4a702d76b 100644 --- a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToRunLengthFeaturesFilter.hxx +++ b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToRunLengthFeaturesFilter.hxx @@ -1,494 +1,662 @@ /*=================================================================== 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. ===================================================================*/ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef __itkEnhancedHistogramToRunLengthFeaturesFilter_hxx #define __itkEnhancedHistogramToRunLengthFeaturesFilter_hxx #include "itkEnhancedHistogramToRunLengthFeaturesFilter.h" #include "itkNumericTraits.h" #include "vnl/vnl_math.h" namespace itk { namespace Statistics { //constructor template EnhancedHistogramToRunLengthFeaturesFilter ::EnhancedHistogramToRunLengthFeaturesFilter() : m_NumberOfVoxels(1) { this->ProcessObject::SetNumberOfRequiredInputs( 1 ); // allocate the data objects for the outputs which are // just decorators real types - for( unsigned int i = 0; i < 12; i++ ) + for( unsigned int i = 0; i < 17; i++ ) { this->ProcessObject::SetNthOutput( i, this->MakeOutput( i ) ); } } template void EnhancedHistogramToRunLengthFeaturesFilter< THistogram> ::SetInput( const HistogramType *histogram ) { this->ProcessObject::SetNthInput( 0, const_cast( histogram ) ); } template const typename EnhancedHistogramToRunLengthFeaturesFilter::HistogramType * EnhancedHistogramToRunLengthFeaturesFilter< THistogram> ::GetInput() const { if ( this->GetNumberOfInputs() < 1 ) { return ITK_NULLPTR; } return itkDynamicCastInDebugMode(this->ProcessObject::GetInput( 0 ) ); } template typename EnhancedHistogramToRunLengthFeaturesFilter::DataObjectPointer EnhancedHistogramToRunLengthFeaturesFilter ::MakeOutput( DataObjectPointerArraySizeType itkNotUsed( idx ) ) { return MeasurementObjectType::New().GetPointer(); } template void EnhancedHistogramToRunLengthFeaturesFilter< THistogram>:: GenerateData( void ) { const HistogramType * inputHistogram = this->GetInput(); this->m_TotalNumberOfRuns = static_cast ( inputHistogram->GetTotalFrequency() ); MeasurementType shortRunEmphasis = NumericTraits::ZeroValue(); MeasurementType longRunEmphasis = NumericTraits::ZeroValue(); MeasurementType greyLevelNonuniformity = NumericTraits::ZeroValue(); MeasurementType runLengthNonuniformity = NumericTraits::ZeroValue(); MeasurementType lowGreyLevelRunEmphasis = NumericTraits::ZeroValue(); MeasurementType highGreyLevelRunEmphasis = NumericTraits::ZeroValue(); MeasurementType shortRunLowGreyLevelEmphasis = NumericTraits::ZeroValue(); MeasurementType shortRunHighGreyLevelEmphasis = NumericTraits::ZeroValue(); MeasurementType longRunLowGreyLevelEmphasis = NumericTraits::ZeroValue(); MeasurementType longRunHighGreyLevelEmphasis = NumericTraits::ZeroValue(); MeasurementType runPercentage = NumericTraits::ZeroValue(); MeasurementType numberOfRuns = NumericTraits::ZeroValue(); + //Added 15.07.2016 + MeasurementType greyLevelVariance = NumericTraits::ZeroValue(); + MeasurementType runLengthVariance = NumericTraits::ZeroValue(); + MeasurementType runEntropy = NumericTraits::ZeroValue(); + + //Added 09.09.2016 + MeasurementType greyLevelNonuniformityNormalized = NumericTraits::ZeroValue(); + MeasurementType runLengthNonuniformityNormalized = NumericTraits::ZeroValue(); vnl_vector greyLevelNonuniformityVector( inputHistogram->GetSize()[0], 0.0 ); vnl_vector runLengthNonuniformityVector( inputHistogram->GetSize()[1], 0.0 ); typedef typename HistogramType::ConstIterator HistogramIterator; + + double mu_i = 0.0; + double mu_j = 0.0; + + //Calculate the means. + for ( HistogramIterator hit = inputHistogram->Begin(); + hit != inputHistogram->End(); ++hit ) + { + IndexType index = hit.GetIndex(); + MeasurementType frequency = hit.GetFrequency(); + + if ( frequency == 0 ) + { + continue; + } + + // MITK_INFO << index[0] + 1 << "|" << index[1] + 1 << " " << frequency; + + MeasurementVectorType measurement = hit.GetMeasurementVector(); + + + double i = index[0] + 1; + double j = index[1] + 1; + + double p_ij = frequency / (1.0*m_TotalNumberOfRuns); + + mu_i += i * p_ij; + mu_j += j * p_ij; + } + // MITK_INFO << "Mu_i " << mu_i << " Mu_j " << mu_j; + //Calculate the other features. + const double log2 = std::log(2.0); + for ( HistogramIterator hit = inputHistogram->Begin(); hit != inputHistogram->End(); ++hit ) { MeasurementType frequency = hit.GetFrequency(); if ( frequency == 0 ) { continue; } MeasurementVectorType measurement = hit.GetMeasurementVector(); IndexType index = hit.GetIndex(); // inputHistogram->GetIndex( hit.GetInstanceIdentifier() ); - double i2 = static_cast( ( index[0] + 1 ) * ( index[0] + 1 ) ); - double j2 = static_cast( ( index[1] + 1 ) * ( index[1] + 1 ) ); + double i = index[0] + 1; + double j = index[1] + 1; + double i2 = i*i; + double j2 = j*j; + + double p_ij = frequency / m_TotalNumberOfRuns; + + greyLevelVariance += ((i - mu_i) * (i - mu_i) * p_ij); + runLengthVariance += ((j - mu_j) * (j - mu_j) * p_ij); + runEntropy -= ( p_ij > 0.0001 ) ? p_ij *std::log(p_ij) / log2 : 0; // Traditional measures shortRunEmphasis += ( frequency / j2 ); longRunEmphasis += ( frequency * j2 ); greyLevelNonuniformityVector[index[0]] += frequency; runLengthNonuniformityVector[index[1]] += frequency; // measures from Chu et al. - lowGreyLevelRunEmphasis += ( frequency / i2 ); + lowGreyLevelRunEmphasis += (i2 > 0.0001) ? ( frequency / i2 ) : 0; highGreyLevelRunEmphasis += ( frequency * i2 ); // measures from Dasarathy and Holder - shortRunLowGreyLevelEmphasis += ( frequency / ( i2 * j2 ) ); - shortRunHighGreyLevelEmphasis += ( frequency * i2 / j2 ); - longRunLowGreyLevelEmphasis += ( frequency * j2 / i2 ); + shortRunLowGreyLevelEmphasis += ((i2 * j2) > 0.0001) ? ( frequency / ( i2 * j2 ) ) : 0; + shortRunHighGreyLevelEmphasis += (j2 > 0.0001) ? ( frequency * i2 / j2 ) : 0; + longRunLowGreyLevelEmphasis += (i2 > 0.0001) ? ( frequency * j2 / i2 ) : 0; longRunHighGreyLevelEmphasis += ( frequency * i2 * j2 ); } greyLevelNonuniformity = greyLevelNonuniformityVector.squared_magnitude(); runLengthNonuniformity = runLengthNonuniformityVector.squared_magnitude(); // Normalize all measures by the total number of runs if (this->m_TotalNumberOfRuns > 0) { shortRunEmphasis /= static_cast( this->m_TotalNumberOfRuns ); longRunEmphasis /= static_cast( this->m_TotalNumberOfRuns ); greyLevelNonuniformity /= static_cast( this->m_TotalNumberOfRuns ); runLengthNonuniformity /= static_cast( this->m_TotalNumberOfRuns ); lowGreyLevelRunEmphasis /= static_cast( this->m_TotalNumberOfRuns ); highGreyLevelRunEmphasis /= static_cast( this->m_TotalNumberOfRuns ); shortRunLowGreyLevelEmphasis /= static_cast( this->m_TotalNumberOfRuns ); shortRunHighGreyLevelEmphasis /= static_cast( this->m_TotalNumberOfRuns ); longRunLowGreyLevelEmphasis /= static_cast( this->m_TotalNumberOfRuns ); longRunHighGreyLevelEmphasis /= static_cast( this->m_TotalNumberOfRuns ); runPercentage = static_cast( this->m_TotalNumberOfRuns ) / static_cast( this->m_NumberOfVoxels ); numberOfRuns = static_cast( this->m_TotalNumberOfRuns ) ; + + greyLevelNonuniformityNormalized = greyLevelNonuniformity / static_cast(this->m_TotalNumberOfRuns); + runLengthNonuniformityNormalized = runLengthNonuniformity / static_cast(this->m_TotalNumberOfRuns); + } else { shortRunEmphasis = 0; longRunEmphasis = 0; greyLevelNonuniformity = 0; runLengthNonuniformity= 0; lowGreyLevelRunEmphasis = 0; highGreyLevelRunEmphasis = 0; shortRunLowGreyLevelEmphasis = 0; shortRunHighGreyLevelEmphasis= 0; longRunLowGreyLevelEmphasis = 0; longRunHighGreyLevelEmphasis = 0; runPercentage = 0; + + greyLevelNonuniformityNormalized = 0; + runLengthNonuniformityNormalized = 0; + numberOfRuns = static_cast( this->m_TotalNumberOfRuns ) ; } MeasurementObjectType* shortRunEmphasisOutputObject = static_cast( this->ProcessObject::GetOutput( 0 ) ); shortRunEmphasisOutputObject->Set( shortRunEmphasis ); MeasurementObjectType* longRunEmphasisOutputObject = static_cast( this->ProcessObject::GetOutput( 1 ) ); longRunEmphasisOutputObject->Set( longRunEmphasis ); MeasurementObjectType* greyLevelNonuniformityOutputObject = static_cast( this->ProcessObject::GetOutput( 2 ) ); greyLevelNonuniformityOutputObject->Set( greyLevelNonuniformity ); + MeasurementObjectType* greyLevelNonuniformityNormalizedOutputObject = + static_cast( this->ProcessObject::GetOutput( 15 ) ); + greyLevelNonuniformityNormalizedOutputObject->Set( greyLevelNonuniformityNormalized ); + + MeasurementObjectType* runLengthNonuniformityNormalizedOutputObject = + static_cast( this->ProcessObject::GetOutput( 16 ) ); + runLengthNonuniformityNormalizedOutputObject->Set( runLengthNonuniformityNormalized ); + MeasurementObjectType* runLengthNonuniformityOutputObject = static_cast( this->ProcessObject::GetOutput( 3 ) ); runLengthNonuniformityOutputObject->Set( runLengthNonuniformity ); MeasurementObjectType* lowGreyLevelRunEmphasisOutputObject = static_cast( this->ProcessObject::GetOutput( 4 ) ); lowGreyLevelRunEmphasisOutputObject->Set( lowGreyLevelRunEmphasis ); MeasurementObjectType* highGreyLevelRunEmphasisOutputObject = static_cast( this->ProcessObject::GetOutput( 5 ) ); highGreyLevelRunEmphasisOutputObject->Set( highGreyLevelRunEmphasis ); MeasurementObjectType* shortRunLowGreyLevelEmphasisOutputObject = static_cast( this->ProcessObject::GetOutput( 6 ) ); shortRunLowGreyLevelEmphasisOutputObject->Set( shortRunLowGreyLevelEmphasis ); MeasurementObjectType* shortRunHighGreyLevelEmphasisOutputObject = static_cast( this->ProcessObject::GetOutput( 7 ) ); shortRunHighGreyLevelEmphasisOutputObject->Set( shortRunHighGreyLevelEmphasis ); MeasurementObjectType* longRunLowGreyLevelEmphasisOutputObject = static_cast( this->ProcessObject::GetOutput( 8 ) ); longRunLowGreyLevelEmphasisOutputObject->Set( longRunLowGreyLevelEmphasis ); MeasurementObjectType* longRunHighGreyLevelEmphasisOutputObject = static_cast( this->ProcessObject::GetOutput( 9 ) ); longRunHighGreyLevelEmphasisOutputObject->Set( longRunHighGreyLevelEmphasis ); MeasurementObjectType* runPercentagesOutputObject = static_cast( this->ProcessObject::GetOutput( 10 ) ); runPercentagesOutputObject->Set( runPercentage ); MeasurementObjectType* numberOfRunsOutputObject = static_cast( this->ProcessObject::GetOutput( 11 ) ); numberOfRunsOutputObject->Set( numberOfRuns ); + + MeasurementObjectType* greyLevelVarianceOutputObject = + static_cast( this->ProcessObject::GetOutput( 12 ) ); + greyLevelVarianceOutputObject->Set( greyLevelVariance ); + + MeasurementObjectType* runLengthVarianceOutputObject = + static_cast( this->ProcessObject::GetOutput( 13 ) ); + runLengthVarianceOutputObject->Set( runLengthVariance ); + + MeasurementObjectType* runEntropyOutputObject = + static_cast( this->ProcessObject::GetOutput( 14 ) ); + runEntropyOutputObject->Set( runEntropy ); } template const typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* EnhancedHistogramToRunLengthFeaturesFilter ::GetShortRunEmphasisOutput() const { return itkDynamicCastInDebugMode(this->ProcessObject::GetOutput( 0 ) ); } template const typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* EnhancedHistogramToRunLengthFeaturesFilter ::GetLongRunEmphasisOutput() const { return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 1 ) ); } template const typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* EnhancedHistogramToRunLengthFeaturesFilter ::GetGreyLevelNonuniformityOutput() const { return itkDynamicCastInDebugMode(this->ProcessObject::GetOutput( 2 ) ); } template const typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* EnhancedHistogramToRunLengthFeaturesFilter ::GetRunLengthNonuniformityOutput() const { return itkDynamicCastInDebugMode(this->ProcessObject::GetOutput( 3 ) ); } template const typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* EnhancedHistogramToRunLengthFeaturesFilter ::GetLowGreyLevelRunEmphasisOutput() const { return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 4 ) ); } template const typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* EnhancedHistogramToRunLengthFeaturesFilter ::GetHighGreyLevelRunEmphasisOutput() const { return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 5 ) ); } template const typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* EnhancedHistogramToRunLengthFeaturesFilter ::GetShortRunLowGreyLevelEmphasisOutput() const { return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 6 ) ); } template const typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* EnhancedHistogramToRunLengthFeaturesFilter ::GetShortRunHighGreyLevelEmphasisOutput() const { return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 7 ) ); } template const typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* EnhancedHistogramToRunLengthFeaturesFilter ::GetLongRunLowGreyLevelEmphasisOutput() const { return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 8 ) ); } template const typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* EnhancedHistogramToRunLengthFeaturesFilter ::GetLongRunHighGreyLevelEmphasisOutput() const { return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 9 ) ); } template const typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* EnhancedHistogramToRunLengthFeaturesFilter ::GetRunPercentageOutput() const { return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 10 ) ); } template const typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* EnhancedHistogramToRunLengthFeaturesFilter ::GetNumberOfRunsOutput() const { return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 11 ) ); } + template + const + typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToRunLengthFeaturesFilter + ::GetGreyLevelVarianceOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 12 ) ); + } + + template + const + typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToRunLengthFeaturesFilter + ::GetRunLengthVarianceOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 13 ) ); + } + + template + const + typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToRunLengthFeaturesFilter + ::GetRunEntropyOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 14 ) ); + } + + template + const + typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToRunLengthFeaturesFilter + ::GetGreyLevelNonuniformityNormalizedOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 15 ) ); + } + + template + const + typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToRunLengthFeaturesFilter + ::GetRunLengthNonuniformityNormalizedOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 16 ) ); + } + template typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType EnhancedHistogramToRunLengthFeaturesFilter ::GetShortRunEmphasis() const { return this->GetShortRunEmphasisOutput()->Get(); } template typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType EnhancedHistogramToRunLengthFeaturesFilter ::GetLongRunEmphasis() const { return this->GetLongRunEmphasisOutput()->Get(); } template typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType EnhancedHistogramToRunLengthFeaturesFilter ::GetGreyLevelNonuniformity() const { return this->GetGreyLevelNonuniformityOutput()->Get(); } template typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType EnhancedHistogramToRunLengthFeaturesFilter ::GetRunLengthNonuniformity() const { return this->GetRunLengthNonuniformityOutput()->Get(); } template typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType EnhancedHistogramToRunLengthFeaturesFilter ::GetLowGreyLevelRunEmphasis() const { return this->GetLowGreyLevelRunEmphasisOutput()->Get(); } template typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType EnhancedHistogramToRunLengthFeaturesFilter ::GetHighGreyLevelRunEmphasis() const { return this->GetHighGreyLevelRunEmphasisOutput()->Get(); } template typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType EnhancedHistogramToRunLengthFeaturesFilter ::GetShortRunLowGreyLevelEmphasis() const { return this->GetShortRunLowGreyLevelEmphasisOutput()->Get(); } template typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType EnhancedHistogramToRunLengthFeaturesFilter ::GetShortRunHighGreyLevelEmphasis() const { return this->GetShortRunHighGreyLevelEmphasisOutput()->Get(); } template typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType EnhancedHistogramToRunLengthFeaturesFilter ::GetLongRunLowGreyLevelEmphasis() const { return this->GetLongRunLowGreyLevelEmphasisOutput()->Get(); } template typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType EnhancedHistogramToRunLengthFeaturesFilter ::GetLongRunHighGreyLevelEmphasis() const { return this->GetLongRunHighGreyLevelEmphasisOutput()->Get(); } template typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType EnhancedHistogramToRunLengthFeaturesFilter ::GetRunPercentage() const { return this->GetRunPercentageOutput()->Get(); } template typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType EnhancedHistogramToRunLengthFeaturesFilter ::GetNumberOfRuns() const { return this->GetNumberOfRunsOutput()->Get(); } + template + typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType + EnhancedHistogramToRunLengthFeaturesFilter + ::GetGreyLevelVariance() const + { + return this->GetGreyLevelVarianceOutput()->Get(); + } + template + typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType + EnhancedHistogramToRunLengthFeaturesFilter + ::GetRunLengthVariance() const + { + return this->GetRunLengthVarianceOutput()->Get(); + } + template + typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType + EnhancedHistogramToRunLengthFeaturesFilter + ::GetRunEntropy() const + { + return this->GetRunEntropyOutput()->Get(); + } + + template + typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType + EnhancedHistogramToRunLengthFeaturesFilter + ::GetGreyLevelNonuniformityNormalized() const + { + return this->GetGreyLevelNonuniformityNormalizedOutput()->Get(); + } + template + typename EnhancedHistogramToRunLengthFeaturesFilter::MeasurementType + EnhancedHistogramToRunLengthFeaturesFilter + ::GetRunLengthNonuniformityNormalized() const + { + return this->GetRunLengthNonuniformityNormalizedOutput()->Get(); + } template typename EnhancedHistogramToRunLengthFeaturesFilter< THistogram>::MeasurementType EnhancedHistogramToRunLengthFeaturesFilter ::GetFeature( RunLengthFeatureName feature ) { switch( feature ) { case ShortRunEmphasis: return this->GetShortRunEmphasis(); case LongRunEmphasis: return this->GetLongRunEmphasis(); case GreyLevelNonuniformity: return this->GetGreyLevelNonuniformity(); + case GreyLevelNonuniformityNormalized: + return this->GetGreyLevelNonuniformityNormalized(); case RunLengthNonuniformity: return this->GetRunLengthNonuniformity(); + case RunLengthNonuniformityNormalized: + return this->GetRunLengthNonuniformityNormalized(); case LowGreyLevelRunEmphasis: return this->GetLowGreyLevelRunEmphasis(); case HighGreyLevelRunEmphasis: return this->GetHighGreyLevelRunEmphasis(); case ShortRunLowGreyLevelEmphasis: return this->GetShortRunLowGreyLevelEmphasis(); case ShortRunHighGreyLevelEmphasis: return this->GetShortRunHighGreyLevelEmphasis(); case LongRunLowGreyLevelEmphasis: return this->GetLongRunLowGreyLevelEmphasis(); case LongRunHighGreyLevelEmphasis: return this->GetLongRunHighGreyLevelEmphasis(); case RunPercentage: return this->GetRunPercentage(); case NumberOfRuns: return this->GetNumberOfRuns(); + case GreyLevelVariance: + return this->GetGreyLevelVariance(); + case RunLengthVariance: + return this->GetRunLengthVariance(); + case RunEntropy: + return this->GetRunEntropy(); default: return 0; } } template< typename THistogram> void EnhancedHistogramToRunLengthFeaturesFilter< THistogram>:: PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf( os,indent ); } } // end of namespace Statistics } // end of namespace itk -#endif \ No newline at end of file +#endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToSizeZoneFeaturesFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToSizeZoneFeaturesFilter.h new file mode 100644 index 0000000000..211ec6b406 --- /dev/null +++ b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToSizeZoneFeaturesFilter.h @@ -0,0 +1,240 @@ +/*=================================================================== + +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. + +===================================================================*/ + +/*========================================================================= +* +* Copyright Insight Software Consortium +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0.txt +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*=========================================================================*/ +#ifndef __itkEnhancedHistogramToSizeZoneFeaturesFilter_h +#define __itkEnhancedHistogramToSizeZoneFeaturesFilter_h + +#include "itkHistogram.h" +#include "itkMacro.h" +#include "itkProcessObject.h" +#include "itkSimpleDataObjectDecorator.h" + +namespace itk { + namespace Statistics { + /** \class EnhancedHistogramToSizeZoneFeaturesFilter + * \brief This class computes texture feature coefficients from a grey level + * Zone-length matrix. + * + * By default, Zone length features are computed for each spatial + * direction and then averaged afterward, so it is possible to access the + * standard deviations of the texture features. These values give a clue as + * to texture anisotropy. However, doing this is much more work, because it + * involved computing one for each offset given. To compute a single matrix + * using the first offset, call FastCalculationsOn(). If this is called, + * then the texture standard deviations will not be computed (and will be set + * to zero), but texture computation will be much faster. + * + * This class is templated over the input histogram type. + * + * Print references: + * M. M. Galloway. Texture analysis using gray level Zone lengths. Computer + * Graphics and Image Processing, 4:172-179, 1975. + * + * A. Chu, C. M. Sehgal, and J. F. Greenleaf. Use of gray value distribution of + * Zone lengths for texture analysis. Pattern Recognition Letters, 11:415-420, + * 1990. + * + * B. R. Dasarathy and E. B. Holder. Image characterizations based on joint + * gray-level Zone-length distributions. Pattern Recognition Letters, 12:490-502, + * 1991. + * + * IJ article: http://hdl.handle.net/1926/1374 + * + * \sa ScalarImageToSizeZoneFeaturesFilter + * \sa ScalarImageToSizeZoneMatrixFilter + * \sa EnhancedHistogramToSizeZoneFeaturesFilter + * + * \author: Nick Tustison + * \ingroup ITKStatistics + */ + + template< typename THistogram > + class EnhancedHistogramToSizeZoneFeaturesFilter : public ProcessObject + { + public: + /** Standard typedefs */ + typedef EnhancedHistogramToSizeZoneFeaturesFilter Self; + typedef ProcessObject Superclass; + typedef SmartPointer Pointer; + typedef SmartPointer ConstPointer; + + /** Zone-time type information (and related methods). */ + itkTypeMacro( EnhancedHistogramToSizeZoneFeaturesFilter, ProcessObject ); + + /** standard New() method support */ + itkNewMacro( Self ); + + typedef THistogram HistogramType; + typedef typename HistogramType::Pointer HistogramPointer; + typedef typename HistogramType::ConstPointer HistogramConstPointer; + typedef typename HistogramType::MeasurementType MeasurementType; + typedef typename HistogramType::MeasurementVectorType MeasurementVectorType; + typedef typename HistogramType::IndexType IndexType; + typedef typename HistogramType:: + TotalAbsoluteFrequencyType FrequencyType; + + /** Method to Set/Get the input Histogram */ + using Superclass::SetInput; + void SetInput ( const HistogramType * histogram ); + const HistogramType * GetInput() const; + + /** Smart Pointer type to a DataObject. */ + typedef DataObject::Pointer DataObjectPointer; + + /** Type of DataObjects used for scalar outputs */ + typedef SimpleDataObjectDecorator MeasurementObjectType; + + /** Methods to return the short Zone emphasis. */ + MeasurementType GetSmallZoneEmphasis() const; + const MeasurementObjectType* GetSmallZoneEmphasisOutput() const; + + /** Methods to return the long Zone emphasis. */ + MeasurementType GetLargeZoneEmphasis() const; + const MeasurementObjectType* GetLargeZoneEmphasisOutput() const; + + /** Methods to return the grey level nonuniformity. */ + MeasurementType GetGreyLevelNonuniformity() const; + const MeasurementObjectType* GetGreyLevelNonuniformityOutput() const; + + /** Methods to return the grey level nonuniformity normalized. */ + MeasurementType GetGreyLevelNonuniformityNormalized() const; + const MeasurementObjectType* GetGreyLevelNonuniformityNormalizedOutput() const; + + /** Methods to return the Zone length nonuniformity. */ + MeasurementType GetSizeZoneNonuniformity() const; + const MeasurementObjectType* GetSizeZoneNonuniformityOutput() const; + + /** Methods to return the Zone length nonuniformity normalized. */ + MeasurementType GetSizeZoneNonuniformityNormalized() const; + const MeasurementObjectType* GetSizeZoneNonuniformityNormalizedOutput() const; + + /** Methods to return the low grey level Zone emphasis. */ + MeasurementType GetLowGreyLevelZoneEmphasis() const; + const MeasurementObjectType* GetLowGreyLevelZoneEmphasisOutput() const; + + /** Methods to return the high grey level Zone emphasis. */ + MeasurementType GetHighGreyLevelZoneEmphasis() const; + const MeasurementObjectType* GetHighGreyLevelZoneEmphasisOutput() const; + + /** Methods to return the short Zone low grey level Zone emphasis. */ + MeasurementType GetSmallZoneLowGreyLevelEmphasis() const; + const MeasurementObjectType* GetSmallZoneLowGreyLevelEmphasisOutput() const; + + /** Methods to return the short Zone high grey level Zone emphasis. */ + MeasurementType GetSmallZoneHighGreyLevelEmphasis() const; + const MeasurementObjectType* GetSmallZoneHighGreyLevelEmphasisOutput() const; + + /** Methods to return the long Zone low grey level Zone emphasis. */ + MeasurementType GetLargeZoneLowGreyLevelEmphasis() const; + const MeasurementObjectType* GetLargeZoneLowGreyLevelEmphasisOutput() const; + + /** Methods to return the long Zone high grey level Zone emphasis. */ + MeasurementType GetLargeZoneHighGreyLevelEmphasis() const; + const MeasurementObjectType* GetLargeZoneHighGreyLevelEmphasisOutput() const; + + /** Methods to return the long Zone high grey level Zone emphasis. */ + MeasurementType GetZonePercentage() const; + const MeasurementObjectType* GetZonePercentageOutput() const; + + /** Methods to return the long Zone high grey level Zone emphasis. */ + MeasurementType GetNumberOfZones() const; + const MeasurementObjectType* GetNumberOfZonesOutput() const; + + /** Methods to return the grey level variance. */ + MeasurementType GetGreyLevelVariance() const; + const MeasurementObjectType* GetGreyLevelVarianceOutput() const; + + /** Methods to return the Zone length variance. */ + MeasurementType GetSizeZoneVariance() const; + const MeasurementObjectType* GetSizeZoneVarianceOutput() const; + + /** Methods to return the Zone entropy. */ + MeasurementType GetZoneEntropy() const; + const MeasurementObjectType* GetZoneEntropyOutput() const; + + itkGetMacro( TotalNumberOfZones, unsigned long ); + + itkGetConstMacro(NumberOfVoxels, unsigned long); + itkSetMacro(NumberOfVoxels, unsigned long); + + /** Zone-length feature types */ + typedef enum + { + SmallZoneEmphasis, + LargeZoneEmphasis, + GreyLevelNonuniformity, + GreyLevelNonuniformityNormalized, + SizeZoneNonuniformity, + SizeZoneNonuniformityNormalized, + LowGreyLevelZoneEmphasis, + HighGreyLevelZoneEmphasis, + SmallZoneLowGreyLevelEmphasis, + SmallZoneHighGreyLevelEmphasis, + LargeZoneLowGreyLevelEmphasis, + LargeZoneHighGreyLevelEmphasis, + ZonePercentage, + GreyLevelVariance, + SizeZoneVariance, + ZoneEntropy + } SizeZoneFeatureName; + + /** convenience method to access the Zone length values */ + MeasurementType GetFeature( SizeZoneFeatureName name ); + + protected: + EnhancedHistogramToSizeZoneFeaturesFilter(); + ~EnhancedHistogramToSizeZoneFeaturesFilter() {}; + virtual void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE; + + /** Make a DataObject to be used for output output. */ + typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType; + using Superclass::MakeOutput; + virtual DataObjectPointer MakeOutput( DataObjectPointerArraySizeType ) ITK_OVERRIDE; + + virtual void GenerateData() ITK_OVERRIDE; + + private: + EnhancedHistogramToSizeZoneFeaturesFilter(const Self&); //purposely not implemented + void operator=(const Self&); //purposely not implemented + + unsigned long m_TotalNumberOfZones; + unsigned long m_NumberOfVoxels; + }; + } // end of namespace Statistics +} // end of namespace itk + +#ifndef ITK_MANUAL_INSTANTIATION +#include "itkEnhancedHistogramToSizeZoneFeaturesFilter.hxx" +#endif + +#endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToSizeZoneFeaturesFilter.hxx b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToSizeZoneFeaturesFilter.hxx new file mode 100644 index 0000000000..878f09bcd5 --- /dev/null +++ b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToSizeZoneFeaturesFilter.hxx @@ -0,0 +1,669 @@ +/*=================================================================== + +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. + +===================================================================*/ + +/*========================================================================= +* +* Copyright Insight Software Consortium +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0.txt +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*=========================================================================*/ +#ifndef __itkEnhancedHistogramToSizeZoneFeaturesFilter_hxx +#define __itkEnhancedHistogramToSizeZoneFeaturesFilter_hxx + +#include "itkEnhancedHistogramToSizeZoneFeaturesFilter.h" + +#include "itkNumericTraits.h" +#include "vnl/vnl_math.h" + +namespace itk { + namespace Statistics { + //constructor + template + EnhancedHistogramToSizeZoneFeaturesFilter + ::EnhancedHistogramToSizeZoneFeaturesFilter() : + m_NumberOfVoxels(1) + { + this->ProcessObject::SetNumberOfRequiredInputs( 1 ); + + // allocate the data objects for the outputs which are + // just decorators real types + for( unsigned int i = 0; i < 17; i++ ) + { + this->ProcessObject::SetNthOutput( i, this->MakeOutput( i ) ); + } + } + + template + void + EnhancedHistogramToSizeZoneFeaturesFilter< THistogram> + ::SetInput( const HistogramType *histogram ) + { + this->ProcessObject::SetNthInput( 0, const_cast( histogram ) ); + } + + template + const typename + EnhancedHistogramToSizeZoneFeaturesFilter::HistogramType * + EnhancedHistogramToSizeZoneFeaturesFilter< THistogram> + ::GetInput() const + { + if ( this->GetNumberOfInputs() < 1 ) + { + return ITK_NULLPTR; + } + return itkDynamicCastInDebugMode(this->ProcessObject::GetInput( 0 ) ); + } + + template + typename + EnhancedHistogramToSizeZoneFeaturesFilter::DataObjectPointer + EnhancedHistogramToSizeZoneFeaturesFilter + ::MakeOutput( DataObjectPointerArraySizeType itkNotUsed( idx ) ) + { + return MeasurementObjectType::New().GetPointer(); + } + + template + void + EnhancedHistogramToSizeZoneFeaturesFilter< THistogram>:: + GenerateData( void ) + { + const HistogramType * inputHistogram = this->GetInput(); + + this->m_TotalNumberOfZones = static_cast + ( inputHistogram->GetTotalFrequency() ); + + MeasurementType SmallZoneEmphasis = NumericTraits::ZeroValue(); + MeasurementType LargeZoneEmphasis = NumericTraits::ZeroValue(); + MeasurementType greyLevelNonuniformity = NumericTraits::ZeroValue(); + MeasurementType SizeZoneNonuniformity = NumericTraits::ZeroValue(); + MeasurementType lowGreyLevelZoneEmphasis = NumericTraits::ZeroValue(); + MeasurementType highGreyLevelZoneEmphasis = NumericTraits::ZeroValue(); + MeasurementType SmallZoneLowGreyLevelEmphasis = NumericTraits::ZeroValue(); + MeasurementType SmallZoneHighGreyLevelEmphasis = NumericTraits::ZeroValue(); + MeasurementType LargeZoneLowGreyLevelEmphasis = NumericTraits::ZeroValue(); + MeasurementType LargeZoneHighGreyLevelEmphasis = NumericTraits::ZeroValue(); + MeasurementType ZonePercentage = NumericTraits::ZeroValue(); + MeasurementType numberOfZones = NumericTraits::ZeroValue(); + //Added 15.07.2016 + MeasurementType greyLevelVariance = NumericTraits::ZeroValue(); + MeasurementType SizeZoneVariance = NumericTraits::ZeroValue(); + MeasurementType ZoneEntropy = NumericTraits::ZeroValue(); + + //Added 09.09.2016 + MeasurementType greyLevelNonuniformityNormalized = NumericTraits::ZeroValue(); + MeasurementType SizeZoneNonuniformityNormalized = NumericTraits::ZeroValue(); + + + vnl_vector greyLevelNonuniformityVector( + inputHistogram->GetSize()[0], 0.0 ); + vnl_vector SizeZoneNonuniformityVector( + inputHistogram->GetSize()[1], 0.0 ); + + typedef typename HistogramType::ConstIterator HistogramIterator; + + double mu_i = 0.0; + double mu_j = 0.0; + + //Calculate the means. + for ( HistogramIterator hit = inputHistogram->Begin(); + hit != inputHistogram->End(); ++hit ) + { + MeasurementType frequency = hit.GetFrequency(); + if ( frequency == 0 ) + { + continue; + } + MeasurementVectorType measurement = hit.GetMeasurementVector(); + IndexType index = hit.GetIndex(); + + int value = floor(measurement[0] + 0.5); + int count = measurement[1]; + + double i = value; + double j = count; + + double p_ij = frequency / m_TotalNumberOfZones; + + mu_i += i * p_ij; + mu_j += j * p_ij; + } + + //Calculate the other features. + const double log2 = std::log(2.0); + int totNumOfVoxelsUsed = 0; + + for ( HistogramIterator hit = inputHistogram->Begin(); + hit != inputHistogram->End(); ++hit ) + { + MeasurementType frequency = hit.GetFrequency(); + if ( frequency == 0 ) + { + continue; + } + MeasurementVectorType measurement = hit.GetMeasurementVector(); + IndexType index = hit.GetIndex(); + // inputHistogram->GetIndex( hit.GetInstanceIdentifier() ); + + int value = floor(measurement[0] + 0.5); + int count = measurement[1]; + + double i = value; + double j = count; + + double i2 = static_cast( i*i ); + double j2 = static_cast( j*j ); + + double p_ij = frequency / m_TotalNumberOfZones; + + greyLevelVariance += ((i - mu_i) * (i - mu_i) * p_ij); + SizeZoneVariance += ((j - mu_j) * (j - mu_j) * p_ij); + ZoneEntropy -= ( p_ij > 0.0001 ) ? p_ij *std::log(p_ij) / log2 : 0; + + // Traditional measures + SmallZoneEmphasis += ( frequency / j2 ); + LargeZoneEmphasis += ( frequency * j2 ); + + greyLevelNonuniformityVector[index[0]] += frequency; + SizeZoneNonuniformityVector[index[1]] += frequency; + + // measures from Chu et al. + lowGreyLevelZoneEmphasis += (i2 > 0.0001) ? ( frequency / i2 ) : 0; + highGreyLevelZoneEmphasis += ( frequency * i2 ); + + // measures from Dasarathy and Holder + SmallZoneLowGreyLevelEmphasis += ((i2 * j2) > 0.0001) ? ( frequency / ( i2 * j2 ) ) : 0; + SmallZoneHighGreyLevelEmphasis += (j2 > 0.00001) ? ( frequency * i2 / j2 ) : 0; + LargeZoneLowGreyLevelEmphasis += (i2 > 0.00001) ? ( frequency * j2 / i2 ) : 0; + LargeZoneHighGreyLevelEmphasis += ( frequency * i2 * j2 ); + + totNumOfVoxelsUsed += (frequency); + } + greyLevelNonuniformity = + greyLevelNonuniformityVector.squared_magnitude(); + SizeZoneNonuniformity = + SizeZoneNonuniformityVector.squared_magnitude(); + + // Normalize all measures by the total number of Zones + + m_TotalNumberOfZones = totNumOfVoxelsUsed; + + if (this->m_TotalNumberOfZones > 0) + { + SmallZoneEmphasis /= + static_cast( this->m_TotalNumberOfZones ); + LargeZoneEmphasis /= + static_cast( this->m_TotalNumberOfZones ); + greyLevelNonuniformity /= + static_cast( this->m_TotalNumberOfZones ); + SizeZoneNonuniformity /= + static_cast( this->m_TotalNumberOfZones ); + + lowGreyLevelZoneEmphasis /= + static_cast( this->m_TotalNumberOfZones ); + highGreyLevelZoneEmphasis /= + static_cast( this->m_TotalNumberOfZones ); + + SmallZoneLowGreyLevelEmphasis /= + static_cast( this->m_TotalNumberOfZones ); + SmallZoneHighGreyLevelEmphasis /= + static_cast( this->m_TotalNumberOfZones ); + LargeZoneLowGreyLevelEmphasis /= + static_cast( this->m_TotalNumberOfZones ); + LargeZoneHighGreyLevelEmphasis /= + static_cast( this->m_TotalNumberOfZones ); + ZonePercentage = static_cast( this->m_TotalNumberOfZones ) / static_cast( this->m_NumberOfVoxels ); + numberOfZones = static_cast( this->m_TotalNumberOfZones ) ; + + greyLevelNonuniformityNormalized = greyLevelNonuniformity / static_cast(this->m_TotalNumberOfZones); + SizeZoneNonuniformityNormalized = SizeZoneNonuniformity / static_cast(this->m_TotalNumberOfZones); + + } else { + SmallZoneEmphasis = 0; + LargeZoneEmphasis = 0; + greyLevelNonuniformity = 0; + SizeZoneNonuniformity = 0; + greyLevelNonuniformityNormalized = 0; + SizeZoneNonuniformityNormalized = 0; + + lowGreyLevelZoneEmphasis = 0; + highGreyLevelZoneEmphasis = 0; + + SmallZoneLowGreyLevelEmphasis = 0; + SmallZoneHighGreyLevelEmphasis= 0; + LargeZoneLowGreyLevelEmphasis = 0; + LargeZoneHighGreyLevelEmphasis= 0; + ZonePercentage = 0; + numberOfZones = static_cast( this->m_TotalNumberOfZones ) ; + } + + MeasurementObjectType* SmallZoneEmphasisOutputObject = + static_cast( this->ProcessObject::GetOutput( 0 ) ); + SmallZoneEmphasisOutputObject->Set( SmallZoneEmphasis ); + + MeasurementObjectType* LargeZoneEmphasisOutputObject = + static_cast( this->ProcessObject::GetOutput( 1 ) ); + LargeZoneEmphasisOutputObject->Set( LargeZoneEmphasis ); + + MeasurementObjectType* greyLevelNonuniformityOutputObject = + static_cast( this->ProcessObject::GetOutput( 2 ) ); + greyLevelNonuniformityOutputObject->Set( greyLevelNonuniformity ); + + MeasurementObjectType* SizeZoneNonuniformityOutputObject = + static_cast( this->ProcessObject::GetOutput( 3 ) ); + SizeZoneNonuniformityOutputObject->Set( SizeZoneNonuniformity ); + + MeasurementObjectType* lowGreyLevelZoneEmphasisOutputObject = + static_cast( this->ProcessObject::GetOutput( 4 ) ); + lowGreyLevelZoneEmphasisOutputObject->Set( lowGreyLevelZoneEmphasis ); + + MeasurementObjectType* highGreyLevelZoneEmphasisOutputObject = + static_cast( this->ProcessObject::GetOutput( 5 ) ); + highGreyLevelZoneEmphasisOutputObject->Set( highGreyLevelZoneEmphasis ); + + MeasurementObjectType* SmallZoneLowGreyLevelEmphasisOutputObject = + static_cast( this->ProcessObject::GetOutput( 6 ) ); + SmallZoneLowGreyLevelEmphasisOutputObject->Set( SmallZoneLowGreyLevelEmphasis ); + + MeasurementObjectType* SmallZoneHighGreyLevelEmphasisOutputObject = + static_cast( this->ProcessObject::GetOutput( 7 ) ); + SmallZoneHighGreyLevelEmphasisOutputObject->Set( + SmallZoneHighGreyLevelEmphasis ); + + MeasurementObjectType* LargeZoneLowGreyLevelEmphasisOutputObject = + static_cast( this->ProcessObject::GetOutput( 8 ) ); + LargeZoneLowGreyLevelEmphasisOutputObject->Set( LargeZoneLowGreyLevelEmphasis ); + + MeasurementObjectType* LargeZoneHighGreyLevelEmphasisOutputObject = + static_cast( this->ProcessObject::GetOutput( 9 ) ); + LargeZoneHighGreyLevelEmphasisOutputObject->Set( LargeZoneHighGreyLevelEmphasis ); + + MeasurementObjectType* ZonePercentagesOutputObject = + static_cast( this->ProcessObject::GetOutput( 10 ) ); + ZonePercentagesOutputObject->Set( ZonePercentage ); + + MeasurementObjectType* numberOfZonesOutputObject = + static_cast( this->ProcessObject::GetOutput( 11 ) ); + numberOfZonesOutputObject->Set( numberOfZones ); + + MeasurementObjectType* greyLevelVarianceOutputObject = + static_cast( this->ProcessObject::GetOutput( 12 ) ); + greyLevelVarianceOutputObject->Set( greyLevelVariance ); + + MeasurementObjectType* SizeZoneVarianceOutputObject = + static_cast( this->ProcessObject::GetOutput( 13 ) ); + SizeZoneVarianceOutputObject->Set( SizeZoneVariance ); + + MeasurementObjectType* ZoneEntropyOutputObject = + static_cast( this->ProcessObject::GetOutput( 14 ) ); + ZoneEntropyOutputObject->Set( ZoneEntropy ); + + MeasurementObjectType* greyLevelNonuniformityNormalizedOutputObject = + static_cast( this->ProcessObject::GetOutput( 15 ) ); + greyLevelNonuniformityNormalizedOutputObject->Set( greyLevelNonuniformityNormalized ); + + MeasurementObjectType* SizeZoneNonuniformityNormalizedOutputObject = + static_cast( this->ProcessObject::GetOutput( 16 ) ); + SizeZoneNonuniformityNormalizedOutputObject->Set( SizeZoneNonuniformityNormalized ); + + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetSmallZoneEmphasisOutput() const + { + return itkDynamicCastInDebugMode(this->ProcessObject::GetOutput( 0 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetLargeZoneEmphasisOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 1 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetGreyLevelNonuniformityOutput() const + { + return itkDynamicCastInDebugMode(this->ProcessObject::GetOutput( 2 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetSizeZoneNonuniformityOutput() const + { + return itkDynamicCastInDebugMode(this->ProcessObject::GetOutput( 3 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetGreyLevelNonuniformityNormalizedOutput() const + { + return itkDynamicCastInDebugMode(this->ProcessObject::GetOutput( 15 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetSizeZoneNonuniformityNormalizedOutput() const + { + return itkDynamicCastInDebugMode(this->ProcessObject::GetOutput( 16 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetLowGreyLevelZoneEmphasisOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 4 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetHighGreyLevelZoneEmphasisOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 5 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetSmallZoneLowGreyLevelEmphasisOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 6 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetSmallZoneHighGreyLevelEmphasisOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 7 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetLargeZoneLowGreyLevelEmphasisOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 8 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetLargeZoneHighGreyLevelEmphasisOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 9 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetZonePercentageOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 10 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetNumberOfZonesOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 11 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetGreyLevelVarianceOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 12 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetSizeZoneVarianceOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 13 ) ); + } + + template + const + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementObjectType* + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetZoneEntropyOutput() const + { + return itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 14 ) ); + } + + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetSmallZoneEmphasis() const + { + return this->GetSmallZoneEmphasisOutput()->Get(); + } + + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetLargeZoneEmphasis() const + { + return this->GetLargeZoneEmphasisOutput()->Get(); + } + + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetGreyLevelNonuniformity() const + { + return this->GetGreyLevelNonuniformityOutput()->Get(); + } + + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetGreyLevelNonuniformityNormalized() const + { + return this->GetGreyLevelNonuniformityNormalizedOutput()->Get(); + } + + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetSizeZoneNonuniformity() const + { + return this->GetSizeZoneNonuniformityOutput()->Get(); + } + + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetSizeZoneNonuniformityNormalized() const + { + return this->GetSizeZoneNonuniformityNormalizedOutput()->Get(); + } + + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetLowGreyLevelZoneEmphasis() const + { + return this->GetLowGreyLevelZoneEmphasisOutput()->Get(); + } + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetHighGreyLevelZoneEmphasis() const + { + return this->GetHighGreyLevelZoneEmphasisOutput()->Get(); + } + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetSmallZoneLowGreyLevelEmphasis() const + { + return this->GetSmallZoneLowGreyLevelEmphasisOutput()->Get(); + } + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetSmallZoneHighGreyLevelEmphasis() const + { + return this->GetSmallZoneHighGreyLevelEmphasisOutput()->Get(); + } + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetLargeZoneLowGreyLevelEmphasis() const + { + return this->GetLargeZoneLowGreyLevelEmphasisOutput()->Get(); + } + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetLargeZoneHighGreyLevelEmphasis() const + { + return this->GetLargeZoneHighGreyLevelEmphasisOutput()->Get(); + } + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetZonePercentage() const + { + return this->GetZonePercentageOutput()->Get(); + } + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetNumberOfZones() const + { + return this->GetNumberOfZonesOutput()->Get(); + } + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetGreyLevelVariance() const + { + return this->GetGreyLevelVarianceOutput()->Get(); + } + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetSizeZoneVariance() const + { + return this->GetSizeZoneVarianceOutput()->Get(); + } + template + typename EnhancedHistogramToSizeZoneFeaturesFilter::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetZoneEntropy() const + { + return this->GetZoneEntropyOutput()->Get(); + } + + + template + typename EnhancedHistogramToSizeZoneFeaturesFilter< THistogram>::MeasurementType + EnhancedHistogramToSizeZoneFeaturesFilter + ::GetFeature( SizeZoneFeatureName feature ) + { + switch( feature ) + { + case SmallZoneEmphasis: + return this->GetSmallZoneEmphasis(); + case LargeZoneEmphasis: + return this->GetLargeZoneEmphasis(); + case GreyLevelNonuniformity: + return this->GetGreyLevelNonuniformity(); + case GreyLevelNonuniformityNormalized: + return this->GetGreyLevelNonuniformityNormalized(); + case SizeZoneNonuniformity: + return this->GetSizeZoneNonuniformity(); + case SizeZoneNonuniformityNormalized: + return this->GetSizeZoneNonuniformityNormalized(); + case LowGreyLevelZoneEmphasis: + return this->GetLowGreyLevelZoneEmphasis(); + case HighGreyLevelZoneEmphasis: + return this->GetHighGreyLevelZoneEmphasis(); + case SmallZoneLowGreyLevelEmphasis: + return this->GetSmallZoneLowGreyLevelEmphasis(); + case SmallZoneHighGreyLevelEmphasis: + return this->GetSmallZoneHighGreyLevelEmphasis(); + case LargeZoneLowGreyLevelEmphasis: + return this->GetLargeZoneLowGreyLevelEmphasis(); + case LargeZoneHighGreyLevelEmphasis: + return this->GetLargeZoneHighGreyLevelEmphasis(); + case ZonePercentage: + return this->GetZonePercentage(); + case GreyLevelVariance: + return this->GetGreyLevelVariance(); + case SizeZoneVariance: + return this->GetSizeZoneVariance(); + case ZoneEntropy: + return this->GetZoneEntropy(); + default: + return 0; + } + } + + template< typename THistogram> + void + EnhancedHistogramToSizeZoneFeaturesFilter< THistogram>:: + PrintSelf(std::ostream& os, Indent indent) const + { + Superclass::PrintSelf( os,indent ); + } + } // end of namespace Statistics +} // end of namespace itk + +#endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.h index d20f1d148e..0bdae76994 100644 --- a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.h +++ b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.h @@ -1,272 +1,278 @@ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef __itkEnhancedHistogramToTextureFeaturesFilter_h #define __itkEnhancedHistogramToTextureFeaturesFilter_h #include "itkHistogram.h" #include "itkMacro.h" #include "itkProcessObject.h" #include "itkSimpleDataObjectDecorator.h" /** Get built-in type. Creates member Get"name"() (e.g., GetVisibility()); */ #define itkMacroGLCMFeatureGetter(name) \ const MeasurementObjectType * Get##name##Output() const; \ \ MeasurementType Get##name() const; namespace itk { namespace Statistics { /** \class EnhancedHistogramToTextureFeaturesFilter * \brief This class computes texture feature coefficients from a grey level * co-occurrence matrix. * * This class computes features that summarize image texture, given a grey level * co-occurrence matrix (generated by a ScalarImageToCooccurrenceMatrixFilter * or related class). * * The features calculated are as follows (where \f$ g(i, j) \f$ is the element in * cell i, j of a a normalized GLCM): * * "Energy" \f$ = f_1 = \sum_{i,j}g(i, j)^2 \f$ * * "Entropy" \f$ = f_2 = -\sum_{i,j}g(i, j) \log_2 g(i, j)\f$, or 0 if \f$g(i, j) = 0\f$ * * "Correlation" \f$ = f_3 = \sum_{i,j}\frac{(i - \mu)(j - \mu)g(i, j)}{\sigma^2} \f$ * * "Difference Moment" \f$= f_4 = \sum_{i,j}\frac{1}{1 + (i - j)^2}g(i, j) \f$ * * "Inertia" \f$ = f_5 = \sum_{i,j}(i - j)^2g(i, j) \f$ (sometimes called "contrast.") * * "Cluster Shade" \f$ = f_6 = \sum_{i,j}((i - \mu) + (j - \mu))^3 g(i, j) \f$ * * "Cluster Prominence" \f$ = f_7 = \sum_{i,j}((i - \mu) + (j - \mu))^4 g(i, j) \f$ * * "Haralick's Correlation" \f$ = f_8 = \frac{\sum_{i,j}(i, j) g(i, j) -\mu_t^2}{\sigma_t^2} \f$ * where \f$\mu_t\f$ and \f$\sigma_t\f$ are the mean and standard deviation of the row * (or column, due to symmetry) sums. * * Above, \f$ \mu = \f$ (weighted pixel average) \f$ = \sum_{i,j}i \cdot g(i, j) = * \sum_{i,j}j \cdot g(i, j) \f$ (due to matrix summetry), and * * \f$ \sigma = \f$ (weighted pixel variance) \f$ = \sum_{i,j}(i - \mu)^2 \cdot g(i, j) = * \sum_{i,j}(j - \mu)^2 \cdot g(i, j) \f$ (due to matrix summetry) * * A good texture feature set to use is the Conners, Trivedi and Harlow set: * features 1, 2, 4, 5, 6, and 7. There is some correlation between the various * features, so using all of them at the same time is not necessarialy a good idea. * * NOTA BENE: The input histogram will be forcably normalized! * This algorithm takes three passes through the input * histogram if the histogram was already normalized, and four if not. * * Web references: * * http://www.cssip.uq.edu.au/meastex/www/algs/algs/algs.html * http://www.ucalgary.ca/~mhallbey/texture/texture_tutorial.html * * Print references: * * Haralick, R.M., K. Shanmugam and I. Dinstein. 1973. Textural Features for * Image Classification. IEEE Transactions on Systems, Man and Cybernetics. * SMC-3(6):610-620. * * Haralick, R.M. 1979. Statistical and Structural Approaches to Texture. * Proceedings of the IEEE, 67:786-804. * * R.W. Conners and C.A. Harlow. A Theoretical Comaprison of Texture Algorithms. * IEEE Transactions on Pattern Analysis and Machine Intelligence, 2:204-222, 1980. * * R.W. Conners, M.M. Trivedi, and C.A. Harlow. Segmentation of a High-Resolution * Urban Scene using Texture Operators. Computer Vision, Graphics and Image * Processing, 25:273-310, 1984. * * \sa ScalarImageToCooccurrenceMatrixFilter * \sa ScalarImageToTextureFeaturesFilter * * Author: Zachary Pincus * \ingroup ITKStatistics */ template< typename THistogram > class EnhancedHistogramToTextureFeaturesFilter:public ProcessObject { public: /** Standard typedefs */ typedef EnhancedHistogramToTextureFeaturesFilter Self; typedef ProcessObject Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro(EnhancedHistogramToTextureFeaturesFilter, ProcessObject); /** standard New() method support */ itkNewMacro(Self); typedef THistogram HistogramType; typedef typename HistogramType::Pointer HistogramPointer; typedef typename HistogramType::ConstPointer HistogramConstPointer; typedef typename HistogramType::MeasurementType MeasurementType; typedef typename HistogramType::MeasurementVectorType MeasurementVectorType; typedef typename HistogramType::IndexType IndexType; typedef typename HistogramType::AbsoluteFrequencyType AbsoluteFrequencyType; typedef typename HistogramType::RelativeFrequencyType RelativeFrequencyType; typedef typename HistogramType::TotalAbsoluteFrequencyType TotalAbsoluteFrequencyType; typedef typename HistogramType::TotalRelativeFrequencyType TotalRelativeFrequencyType; /** Container to hold relative frequencies of the histogram */ typedef std::vector< RelativeFrequencyType > RelativeFrequencyContainerType; /** Method to Set/Get the input Histogram */ using Superclass::SetInput; void SetInput(const HistogramType *histogram); const HistogramType * GetInput() const; /** Smart Pointer type to a DataObject. */ typedef DataObject::Pointer DataObjectPointer; /** Type of DataObjects used for scalar outputs */ typedef SimpleDataObjectDecorator< MeasurementType > MeasurementObjectType; /** Return energy texture value. */ MeasurementType GetEnergy() const; const MeasurementObjectType * GetEnergyOutput() const; /** Return entropy texture value. */ MeasurementType GetEntropy() const; const MeasurementObjectType * GetEntropyOutput() const; /** return correlation texture value. */ MeasurementType GetCorrelation() const; const MeasurementObjectType * GetCorrelationOutput() const; /** Return inverse difference moment texture value. */ MeasurementType GetInverseDifferenceMoment() const; const MeasurementObjectType * GetInverseDifferenceMomentOutput() const; /** Return inertia texture value. */ MeasurementType GetInertia() const; const MeasurementObjectType * GetInertiaOutput() const; /** Return cluster shade texture value. */ MeasurementType GetClusterShade() const; const MeasurementObjectType * GetClusterShadeOutput() const; /** Return cluster prominence texture value. */ MeasurementType GetClusterProminence() const; const MeasurementObjectType * GetClusterProminenceOutput() const; /** Return Haralick correlation texture value. */ MeasurementType GetHaralickCorrelation() const; const MeasurementObjectType * GetHaralickCorrelationOutput() const; itkMacroGLCMFeatureGetter(Autocorrelation); itkMacroGLCMFeatureGetter(Contrast); itkMacroGLCMFeatureGetter(Dissimilarity); itkMacroGLCMFeatureGetter(MaximumProbability); itkMacroGLCMFeatureGetter(InverseVariance); itkMacroGLCMFeatureGetter(Homogeneity1); itkMacroGLCMFeatureGetter(ClusterTendency); itkMacroGLCMFeatureGetter(Variance); itkMacroGLCMFeatureGetter(SumAverage); itkMacroGLCMFeatureGetter(SumEntropy); itkMacroGLCMFeatureGetter(SumVariance); itkMacroGLCMFeatureGetter(DifferenceAverage); itkMacroGLCMFeatureGetter(DifferenceEntropy); itkMacroGLCMFeatureGetter(DifferenceVariance); itkMacroGLCMFeatureGetter(InverseDifferenceMomentNormalized); itkMacroGLCMFeatureGetter(InverseDifferenceNormalized); itkMacroGLCMFeatureGetter(InverseDifference); + itkMacroGLCMFeatureGetter(JointAverage); + itkMacroGLCMFeatureGetter(FirstMeasureOfInformationCorrelation); + itkMacroGLCMFeatureGetter(SecondMeasureOfInformationCorrelation); /** Texture feature types */ typedef enum { Energy, Entropy, Correlation, InverseDifferenceMoment, Inertia, ClusterShade, ClusterProminence, HaralickCorrelation, Autocorrelation, Contrast, Dissimilarity, MaximumProbability, InverseVariance, Homogeneity1, ClusterTendency, Variance, SumAverage, SumEntropy, SumVariance, DifferenceAverage, DifferenceEntropy, DifferenceVariance, InverseDifferenceMomentNormalized, InverseDifferenceNormalized, InverseDifference, + JointAverage, + FirstMeasureOfInformationCorrelation, + SecondMeasureOfInformationCorrelation, InvalidFeatureName } TextureFeatureName; /** convenience method to access the texture values */ MeasurementType GetFeature(TextureFeatureName name); protected: EnhancedHistogramToTextureFeaturesFilter(); ~EnhancedHistogramToTextureFeaturesFilter() override {} void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE; /** Make a DataObject to be used for output output. */ typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType; using Superclass::MakeOutput; DataObjectPointer MakeOutput(DataObjectPointerArraySizeType) ITK_OVERRIDE; void GenerateData() ITK_OVERRIDE; private: EnhancedHistogramToTextureFeaturesFilter(const Self &); //purposely not implemented void operator=(const Self &); //purposely not implemented void ComputeMeansAndVariances(double & pixelMean, double & marginalMean, double & marginalDevSquared, double & pixelVariance); RelativeFrequencyContainerType m_RelativeFrequencyContainer; }; } // end of namespace Statistics } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkEnhancedHistogramToTextureFeaturesFilter.hxx" #endif #endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.hxx b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.hxx index 78e9de579f..ff154fcf33 100644 --- a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.hxx +++ b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.hxx @@ -1,658 +1,760 @@ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef __itkEnhancedHistogramToTextureFeaturesFilter_hxx #define __itkEnhancedHistogramToTextureFeaturesFilter_hxx #include "itkEnhancedHistogramToTextureFeaturesFilter.h" #include "itkNumericTraits.h" #include "vnl/vnl_math.h" #include "itkMath.h" #define itkMacroGLCMFeature(name, id) \ template< typename THistogram > \ const \ typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * \ EnhancedHistogramToTextureFeaturesFilter< THistogram > \ ::Get##name##Output() const \ { \ return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(id) ); \ -} \ + } \ \ template< typename THistogram > \ typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType \ EnhancedHistogramToTextureFeaturesFilter< THistogram > \ ::Get##name() const \ { \ return this->Get##name##Output()->Get(); \ -} + } namespace itk { - namespace Statistics +namespace Statistics +{ +//constructor +template< typename THistogram > +EnhancedHistogramToTextureFeaturesFilter< THistogram >::EnhancedHistogramToTextureFeaturesFilter(void) +{ + this->ProcessObject::SetNumberOfRequiredInputs(1); + + // allocate the data objects for the outputs which are + // just decorators real types + for ( int i = 0; i < 28; ++i ) { - //constructor - template< typename THistogram > - EnhancedHistogramToTextureFeaturesFilter< THistogram >::EnhancedHistogramToTextureFeaturesFilter(void) + this->ProcessObject::SetNthOutput( i, this->MakeOutput(i) ); + } +} + +template< typename THistogram > +void +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::SetInput(const HistogramType *histogram) +{ + this->ProcessObject::SetNthInput( 0, const_cast< HistogramType * >( histogram ) ); +} + +template< typename THistogram > +const typename +EnhancedHistogramToTextureFeaturesFilter< THistogram >::HistogramType * +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetInput() const +{ + return itkDynamicCastInDebugMode< const HistogramType * >( this->GetPrimaryInput() ); +} + +template< typename THistogram > +typename +EnhancedHistogramToTextureFeaturesFilter< THistogram >::DataObjectPointer +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::MakeOutput( DataObjectPointerArraySizeType itkNotUsed(idx) ) +{ + return MeasurementObjectType::New().GetPointer(); +} + +template< typename THistogram > +void +EnhancedHistogramToTextureFeaturesFilter< THistogram >::GenerateData(void) +{ + typedef typename HistogramType::ConstIterator HistogramIterator; + + const HistogramType *inputHistogram = this->GetInput(); + + //Normalize the absolute frequencies and populate the relative frequency + //container + TotalRelativeFrequencyType totalFrequency = + static_cast< TotalRelativeFrequencyType >( inputHistogram->GetTotalFrequency() ); + + m_RelativeFrequencyContainer.clear(); + + typename HistogramType::SizeValueType binsPerAxis = inputHistogram->GetSize(0); + std::vector sumP; + std::vector diffP; + + sumP.resize(2*binsPerAxis,0.0); + diffP.resize(binsPerAxis,0.0); + + double numberOfPixels = 0; + + for ( HistogramIterator hit = inputHistogram->Begin(); + hit != inputHistogram->End(); ++hit ) + { + AbsoluteFrequencyType frequency = hit.GetFrequency(); + RelativeFrequencyType relativeFrequency = (totalFrequency > 0) ? frequency / totalFrequency : 0; + m_RelativeFrequencyContainer.push_back(relativeFrequency); + + IndexType index = inputHistogram->GetIndex( hit.GetInstanceIdentifier() ); + sumP[index[0] + index[1]] += relativeFrequency; + diffP[std::abs(index[0] - index[1])] += relativeFrequency; + + //if (index[1] == 0) + numberOfPixels += frequency; + } + + // Now get the various means and variances. This is takes two passes + // through the histogram. + double pixelMean; + double marginalMean; + double marginalDevSquared; + double pixelVariance; + + this->ComputeMeansAndVariances(pixelMean, marginalMean, marginalDevSquared, + pixelVariance); + + // Finally compute the texture features. Another one pass. + MeasurementType energy = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType entropy = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType correlation = NumericTraits< MeasurementType >::ZeroValue(); + + MeasurementType inverseDifferenceMoment = + NumericTraits< MeasurementType >::ZeroValue(); + + MeasurementType inertia = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType clusterShade = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType clusterProminence = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType haralickCorrelation = NumericTraits< MeasurementType >::ZeroValue(); + + MeasurementType autocorrelation = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType contrast = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType dissimilarity = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType maximumProbability = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType inverseVariance = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType homogeneity1 = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType clusterTendency = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType variance = NumericTraits< MeasurementType >::ZeroValue(); + + MeasurementType sumAverage = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType sumEntropy = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType sumVariance = NumericTraits< MeasurementType >::ZeroValue(); + + MeasurementType diffAverage = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType diffEntropy = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType diffVariance = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType inverseDifferenceMomentNormalized = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType inverseDifferenceNormalized = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType inverseDifference = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType averageProbability = NumericTraits< MeasurementType >::ZeroValue(); + + MeasurementType firstMeasureOfInformationCorrelation = NumericTraits< MeasurementType >::ZeroValue(); + MeasurementType secondMeasureOfInformationCorrelation = NumericTraits< MeasurementType >::ZeroValue(); + + double pixelVarianceSquared = pixelVariance * pixelVariance; + // Variance is only used in correlation. If variance is 0, then + // (index[0] - pixelMean) * (index[1] - pixelMean) + // should be zero as well. In this case, set the variance to 1. in + // order to avoid NaN correlation. + if( Math::FloatAlmostEqual( pixelVarianceSquared, 0.0, 4, 2*NumericTraits::epsilon() ) ) + { + pixelVarianceSquared = 1.; + } + const double log2 = std::log(2.0); + + typename RelativeFrequencyContainerType::const_iterator rFreqIterator = + m_RelativeFrequencyContainer.begin(); + + IndexType globalIndex(2); + + for ( HistogramIterator hit = inputHistogram->Begin(); + hit != inputHistogram->End(); ++hit ) + { + RelativeFrequencyType frequency = *rFreqIterator; + ++rFreqIterator; + if ( frequency == 0 ) { - this->ProcessObject::SetNumberOfRequiredInputs(1); - - // allocate the data objects for the outputs which are - // just decorators real types - for ( int i = 0; i < 25; ++i ) - { - this->ProcessObject::SetNthOutput( i, this->MakeOutput(i) ); - } + continue; // no use doing these calculations if we're just multiplying by + // zero. } - template< typename THistogram > - void - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::SetInput(const HistogramType *histogram) + IndexType index = inputHistogram->GetIndex( hit.GetInstanceIdentifier() ); + globalIndex = index; + energy += frequency * frequency; + entropy -= ( frequency > 0.0001 ) ? frequency *std::log(frequency) / log2:0; + correlation += ( ( index[0] - pixelMean ) * ( index[1] - pixelMean ) * frequency ) + / pixelVarianceSquared; + inverseDifferenceMoment += frequency + / ( 1.0 + ( index[0] - index[1] ) * ( index[0] - index[1] ) ); + inertia += ( index[0] - index[1] ) * ( index[0] - index[1] ) * frequency; + clusterShade += std::pow( ( index[0] - pixelMean ) + ( index[1] - pixelMean ), 3 ) + * frequency; + clusterProminence += std::pow( ( index[0] - pixelMean ) + ( index[1] - pixelMean ), 4 ) + * frequency; + haralickCorrelation += index[0] * index[1] * frequency; + + // New Features added for Aerts compatibility + autocorrelation +=index[0] * index[1] * frequency; + contrast += (index[0] - index[1]) * (index[0] - index[1]) * frequency; + dissimilarity += std::abs(index[0] - index[1]) * frequency; + maximumProbability +=std::max(maximumProbability, frequency); + averageProbability += frequency * index[0]; + if (index[0] != index[1]) + inverseVariance += frequency / ((index[0] - index[1])*(index[0] - index[1])); + homogeneity1 +=frequency / ( 1.0 + std::abs( index[0] - index[1] )); + clusterTendency += std::pow( ( index[0] - pixelMean ) + ( index[1] - pixelMean ), 2 ) * frequency; + variance += std::pow( ( index[0] - pixelMean ), 2) * frequency; + + if (numberOfPixels > 0) { - this->ProcessObject::SetNthInput( 0, const_cast< HistogramType * >( histogram ) ); + inverseDifferenceMomentNormalized += frequency / ( 1.0 + ( index[0] - index[1] ) * ( index[0] - index[1] ) / numberOfPixels / numberOfPixels); + inverseDifferenceNormalized += frequency / ( 1.0 + std::abs( index[0] - index[1] ) / numberOfPixels ); } - - template< typename THistogram > - const typename - EnhancedHistogramToTextureFeaturesFilter< THistogram >::HistogramType * - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetInput() const + else { - return itkDynamicCastInDebugMode< const HistogramType * >( this->GetPrimaryInput() ); + inverseDifferenceMomentNormalized = 0; + inverseDifferenceNormalized = 0; } + inverseDifference += frequency / ( 1.0 + std::abs( index[0] - index[1] ) ); + } - template< typename THistogram > - typename - EnhancedHistogramToTextureFeaturesFilter< THistogram >::DataObjectPointer - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::MakeOutput( DataObjectPointerArraySizeType itkNotUsed(idx) ) + for (int i = 0; i < (int)sumP.size();++i) + { + double frequency = sumP[i]; + sumAverage += i * frequency; + sumEntropy -= ( frequency > 0.0001 ) ? frequency *std::log(frequency) / log2:0; + } + for (int i = 0; i < (int)sumP.size();++i) + { + double frequency = sumP[i]; + sumVariance += (i-sumAverage)*(i-sumAverage) * frequency; + } + + for (int i = 0; i < (int)diffP.size();++i) + { + double frequency = diffP[i]; + diffAverage += i * frequency; + diffEntropy -= ( frequency > 0.0001 ) ? frequency *std::log(frequency) / log2:0; + } + for (int i = 0; i < (int)diffP.size();++i) + { + double frequency = diffP[i]; + sumVariance += (i-diffAverage)*(i-diffAverage) * frequency; + } + + if (marginalDevSquared > 0) + { + haralickCorrelation = ( haralickCorrelation - marginalMean * marginalMean ) + / marginalDevSquared; + } else + { + haralickCorrelation =0; + } + + //Calculate the margin probs + std::vector pi_margins; + std::vector pj_margins; + + //pi. + for ( std::size_t i = 1; i <= inputHistogram->GetSize(0); i++ ) + { + double pi_tmp= 0.0; + for( std::size_t j = 1; j <= inputHistogram->GetSize(1); j++ ) { - return MeasurementObjectType::New().GetPointer(); + globalIndex[0] = i; + globalIndex[1] = j; + pi_tmp += inputHistogram->GetFrequency(globalIndex); } + pi_margins.push_back(pi_tmp); + } - template< typename THistogram > - void - EnhancedHistogramToTextureFeaturesFilter< THistogram >::GenerateData(void) + //pj. + for ( std::size_t j = 1; j <= inputHistogram->GetSize(1); j++ ) + { + double pj_tmp= 0.0; + for( std::size_t i = 1; i <= inputHistogram->GetSize(0); i++ ) { - typedef typename HistogramType::ConstIterator HistogramIterator; + globalIndex[0] = i; + globalIndex[1] = j; + pj_tmp += inputHistogram->GetFrequency(globalIndex); + } + pj_margins.push_back(pj_tmp); + } - const HistogramType *inputHistogram = this->GetInput(); + //Calculate HX + double hx = 0.0; - //Normalize the absolute frequencies and populate the relative frequency - //container - TotalRelativeFrequencyType totalFrequency = - static_cast< TotalRelativeFrequencyType >( inputHistogram->GetTotalFrequency() ); + for ( std::size_t i = 0; i < inputHistogram->GetSize(0); i++ ) + { + double pi_margin = pi_margins[i]; + hx -= ( pi_margin > 0.0001 ) ? pi_margin *std::log(pi_margin) / log2:0; + } - m_RelativeFrequencyContainer.clear(); + //Calculate HXY1 + double hxy1 = 0.0; - typename HistogramType::SizeValueType binsPerAxis = inputHistogram->GetSize(0); - std::vector sumP; - std::vector diffP; + for ( std::size_t i = 0; i < inputHistogram->GetSize(0); i++ ) + { + for ( std::size_t j = 0; j < inputHistogram->GetSize(1); j++ ) + { + globalIndex[0] = i; + globalIndex[1] = j; - sumP.resize(2*binsPerAxis,0.0); - diffP.resize(binsPerAxis,0.0); + double pi_margin = pi_margins[i]; + double pj_margin = pj_margins[j]; - double numberOfPixels = 0; + double p_ij = inputHistogram->GetFrequency(globalIndex); - for ( HistogramIterator hit = inputHistogram->Begin(); - hit != inputHistogram->End(); ++hit ) - { - AbsoluteFrequencyType frequency = hit.GetFrequency(); - RelativeFrequencyType relativeFrequency = (totalFrequency > 0) ? frequency / totalFrequency : 0; - m_RelativeFrequencyContainer.push_back(relativeFrequency); - - IndexType index = inputHistogram->GetIndex( hit.GetInstanceIdentifier() ); - sumP[index[0] + index[1]] += relativeFrequency; - diffP[std::abs(index[0] - index[1])] += relativeFrequency; - - //if (index[1] == 0) - numberOfPixels += frequency; - } - - // Now get the various means and variances. This is takes two passes - // through the histogram. - double pixelMean; - double marginalMean; - double marginalDevSquared; - double pixelVariance; - - this->ComputeMeansAndVariances(pixelMean, marginalMean, marginalDevSquared, - pixelVariance); - - // Finally compute the texture features. Another one pass. - MeasurementType energy = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType entropy = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType correlation = NumericTraits< MeasurementType >::ZeroValue(); - - MeasurementType inverseDifferenceMoment = - NumericTraits< MeasurementType >::ZeroValue(); - - MeasurementType inertia = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType clusterShade = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType clusterProminence = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType haralickCorrelation = NumericTraits< MeasurementType >::ZeroValue(); - - MeasurementType autocorrelation = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType contrast = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType dissimilarity = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType maximumProbability = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType inverseVariance = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType homogeneity1 = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType clusterTendency = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType variance = NumericTraits< MeasurementType >::ZeroValue(); - - MeasurementType sumAverage = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType sumEntropy = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType sumVariance = NumericTraits< MeasurementType >::ZeroValue(); - - MeasurementType diffAverage = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType diffEntropy = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType diffVariance = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType inverseDifferenceMomentNormalized = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType inverseDifferenceNormalized = NumericTraits< MeasurementType >::ZeroValue(); - MeasurementType inverseDifference = NumericTraits< MeasurementType >::ZeroValue(); - - double pixelVarianceSquared = pixelVariance * pixelVariance; - // Variance is only used in correlation. If variance is 0, then - // (index[0] - pixelMean) * (index[1] - pixelMean) - // should be zero as well. In this case, set the variance to 1. in - // order to avoid NaN correlation. - if( Math::FloatAlmostEqual( pixelVarianceSquared, 0.0, 4, 2*NumericTraits::epsilon() ) ) - { - pixelVarianceSquared = 1.; - } - const double log2 = std::log(2.0); - - typename RelativeFrequencyContainerType::const_iterator rFreqIterator = - m_RelativeFrequencyContainer.begin(); - - MITK_INFO << pixelMean << " - " << pixelVariance; - - for ( HistogramIterator hit = inputHistogram->Begin(); - hit != inputHistogram->End(); ++hit ) - { - RelativeFrequencyType frequency = *rFreqIterator; - ++rFreqIterator; - if ( frequency == 0 ) - { - continue; // no use doing these calculations if we're just multiplying by - // zero. - } - - IndexType index = inputHistogram->GetIndex( hit.GetInstanceIdentifier() ); - energy += frequency * frequency; - entropy -= ( frequency > 0.0001 ) ? frequency *std::log(frequency) / log2:0; - correlation += ( ( index[0] - pixelMean ) * ( index[1] - pixelMean ) * frequency ) - / pixelVarianceSquared; - inverseDifferenceMoment += frequency - / ( 1.0 + ( index[0] - index[1] ) * ( index[0] - index[1] ) ); - inertia += ( index[0] - index[1] ) * ( index[0] - index[1] ) * frequency; - clusterShade += std::pow( ( index[0] - pixelMean ) + ( index[1] - pixelMean ), 3 ) - * frequency; - clusterProminence += std::pow( ( index[0] - pixelMean ) + ( index[1] - pixelMean ), 4 ) - * frequency; - haralickCorrelation += index[0] * index[1] * frequency; - - // New Features added for Aerts compatibility - autocorrelation +=index[0] * index[1] * frequency; - contrast += (index[0] - index[1]) * (index[0] - index[1]) * frequency; - dissimilarity += std::abs(index[0] - index[1]) * frequency; - maximumProbability = std::max(maximumProbability, frequency); - if (index[0] != index[1]) - inverseVariance += frequency / ((index[0] - index[1])*(index[0] - index[1])); - homogeneity1 +=frequency / ( 1.0 + std::abs( index[0] - index[1] )); - clusterTendency += std::pow( ( index[0] - pixelMean ) + ( index[1] - pixelMean ), 2 ) * frequency; - variance += std::pow( ( index[0] - pixelMean ), 2) * frequency; - - if (numberOfPixels > 0) - { - inverseDifferenceMomentNormalized += frequency / ( 1.0 + ( index[0] - index[1] ) * ( index[0] - index[1] ) / numberOfPixels / numberOfPixels); - inverseDifferenceNormalized += frequency / ( 1.0 + std::abs( index[0] - index[1] ) / numberOfPixels ); - } - else - { - inverseDifferenceMomentNormalized = 0; - inverseDifferenceNormalized = 0; - } - inverseDifference += frequency / ( 1.0 + std::abs( index[0] - index[1] ) ); - } - - for (int i = 0; i < (int)sumP.size();++i) - { - double frequency = sumP[i]; - sumAverage += i * frequency; - sumEntropy -= ( frequency > 0.0001 ) ? frequency *std::log(frequency) / log2:0; - } - for (int i = 0; i < (int)sumP.size();++i) - { - double frequency = sumP[i]; - sumVariance += (i-sumAverage)*(i-sumAverage) * frequency; - } - - for (int i = 0; i < (int)diffP.size();++i) - { - double frequency = diffP[i]; - diffAverage += i * frequency; - diffEntropy -= ( frequency > 0.0001 ) ? frequency *std::log(frequency) / log2:0; - } - for (int i = 0; i < (int)diffP.size();++i) - { - double frequency = diffP[i]; - sumVariance += (i-diffAverage)*(i-diffAverage) * frequency; - } - - if (marginalDevSquared > 0) - { - haralickCorrelation = ( haralickCorrelation - marginalMean * marginalMean ) - / marginalDevSquared; - } else - { - haralickCorrelation =0; - } - - MeasurementObjectType *energyOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(0) ); - energyOutputObject->Set(energy); - - MeasurementObjectType *entropyOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(1) ); - entropyOutputObject->Set(entropy); - - MeasurementObjectType *correlationOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(2) ); - correlationOutputObject->Set(correlation); - - MeasurementObjectType *inverseDifferenceMomentOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(3) ); - inverseDifferenceMomentOutputObject->Set(inverseDifferenceMoment); - - MeasurementObjectType *inertiaOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(4) ); - inertiaOutputObject->Set(inertia); - - MeasurementObjectType *clusterShadeOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(5) ); - clusterShadeOutputObject->Set(clusterShade); - - MeasurementObjectType *clusterProminenceOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(6) ); - clusterProminenceOutputObject->Set(clusterProminence); - - MeasurementObjectType *haralickCorrelationOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(7) ); - haralickCorrelationOutputObject->Set(haralickCorrelation); - - MeasurementObjectType *autocorrelationOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(8) ); - autocorrelationOutputObject->Set(autocorrelation); - - MeasurementObjectType *contrastOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(9) ); - contrastOutputObject->Set(contrast); - - MeasurementObjectType *dissimilarityOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(10) ); - dissimilarityOutputObject->Set(dissimilarity); - - MeasurementObjectType *maximumProbabilityOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(11) ); - maximumProbabilityOutputObject->Set(maximumProbability); - - MeasurementObjectType *inverseVarianceOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(12) ); - inverseVarianceOutputObject->Set(inverseVariance); - - MeasurementObjectType *homogeneity1OutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(13) ); - homogeneity1OutputObject->Set(homogeneity1); - - MeasurementObjectType *clusterTendencyOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(14) ); - clusterTendencyOutputObject->Set(clusterTendency); - - MeasurementObjectType *varianceOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(15) ); - varianceOutputObject->Set(variance); - - MeasurementObjectType *sumAverageOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(16) ); - sumAverageOutputObject->Set(sumAverage); - - MeasurementObjectType *sumEntropyOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(17) ); - sumEntropyOutputObject->Set(sumEntropy); - - MeasurementObjectType *sumVarianceOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(18) ); - sumVarianceOutputObject->Set(sumVariance); - - MeasurementObjectType *diffAverageOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(19) ); - diffAverageOutputObject->Set(diffAverage); - - MeasurementObjectType *diffEntropyOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(20) ); - diffEntropyOutputObject->Set(diffEntropy); - - MeasurementObjectType *diffVarianceOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(21) ); - diffVarianceOutputObject->Set(diffVariance); - - MeasurementObjectType *inverseDifferenceMomentNormalizedOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(22) ); - inverseDifferenceMomentNormalizedOutputObject->Set(inverseDifferenceMomentNormalized); - - MeasurementObjectType *inverseDifferenceNormalizedOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(23) ); - inverseDifferenceNormalizedOutputObject->Set(inverseDifferenceNormalized); - - MeasurementObjectType *inverseDifferenceOutputObject = - static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(24) ); - inverseDifferenceOutputObject->Set(inverseDifference); + hxy1 -= ( (pi_margin * pj_margin) > 0.0001 ) ? p_ij *std::log(pi_margin * pj_margin) / log2:0; } + } + + //Calculate HXY2 + double hxy2 = 0.0; - template< typename THistogram > - void - EnhancedHistogramToTextureFeaturesFilter< THistogram >::ComputeMeansAndVariances(double & pixelMean, - double & marginalMean, - double & marginalDevSquared, - double & pixelVariance) + for ( std::size_t i = 0; i < inputHistogram->GetSize(0); i++ ) + { + for ( std::size_t j = 0; j < inputHistogram->GetSize(1); j++ ) { - // This function takes two passes through the histogram and two passes through - // an array of the same length as a histogram axis. This could probably be - // cleverly compressed to one pass, but it's not clear that that's necessary. + globalIndex[0] = i; + globalIndex[1] = j; + + double pi_margin = pi_margins[i]; + double pj_margin = pj_margins[j]; + + hxy1 -= ( (pi_margin * pj_margin) > 0.0001 ) ? (pi_margin * pj_margin) *std::log(pi_margin * pj_margin) / log2:0; + } + } + + firstMeasureOfInformationCorrelation = (entropy - hxy1) / hx; + secondMeasureOfInformationCorrelation = (entropy > hxy2) ? 0 : std::sqrt(1 - std::exp(-2*(hxy2 - entropy))); + + MeasurementObjectType *energyOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(0) ); + energyOutputObject->Set(energy); + + MeasurementObjectType *entropyOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(1) ); + entropyOutputObject->Set(entropy); + + MeasurementObjectType *correlationOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(2) ); + correlationOutputObject->Set(correlation); + + MeasurementObjectType *inverseDifferenceMomentOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(3) ); + inverseDifferenceMomentOutputObject->Set(inverseDifferenceMoment); + + MeasurementObjectType *inertiaOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(4) ); + inertiaOutputObject->Set(inertia); + + MeasurementObjectType *clusterShadeOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(5) ); + clusterShadeOutputObject->Set(clusterShade); + + MeasurementObjectType *clusterProminenceOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(6) ); + clusterProminenceOutputObject->Set(clusterProminence); + + MeasurementObjectType *haralickCorrelationOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(7) ); + haralickCorrelationOutputObject->Set(haralickCorrelation); - typedef typename HistogramType::ConstIterator HistogramIterator; + MeasurementObjectType *autocorrelationOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(8) ); + autocorrelationOutputObject->Set(autocorrelation); - const HistogramType *inputHistogram = this->GetInput(); + MeasurementObjectType *contrastOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(9) ); + contrastOutputObject->Set(contrast); + + MeasurementObjectType *dissimilarityOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(10) ); + dissimilarityOutputObject->Set(dissimilarity); + + MeasurementObjectType *maximumProbabilityOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(11) ); + maximumProbabilityOutputObject->Set(maximumProbability); + + MeasurementObjectType *inverseVarianceOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(12) ); + inverseVarianceOutputObject->Set(inverseVariance); + + MeasurementObjectType *homogeneity1OutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(13) ); + homogeneity1OutputObject->Set(homogeneity1); + + MeasurementObjectType *clusterTendencyOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(14) ); + clusterTendencyOutputObject->Set(clusterTendency); + + MeasurementObjectType *varianceOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(15) ); + varianceOutputObject->Set(variance); + + MeasurementObjectType *sumAverageOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(16) ); + sumAverageOutputObject->Set(sumAverage); + + MeasurementObjectType *sumEntropyOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(17) ); + sumEntropyOutputObject->Set(sumEntropy); + + MeasurementObjectType *sumVarianceOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(18) ); + sumVarianceOutputObject->Set(sumVariance); + + MeasurementObjectType *diffAverageOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(19) ); + diffAverageOutputObject->Set(diffAverage); + + MeasurementObjectType *diffEntropyOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(20) ); + diffEntropyOutputObject->Set(diffEntropy); + + MeasurementObjectType *diffVarianceOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(21) ); + diffVarianceOutputObject->Set(diffVariance); + + MeasurementObjectType *inverseDifferenceMomentNormalizedOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(22) ); + inverseDifferenceMomentNormalizedOutputObject->Set(inverseDifferenceMomentNormalized); + + MeasurementObjectType *inverseDifferenceNormalizedOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(23) ); + inverseDifferenceNormalizedOutputObject->Set(inverseDifferenceNormalized); + + MeasurementObjectType *inverseDifferenceOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(24) ); + inverseDifferenceOutputObject->Set(inverseDifference); + + MeasurementObjectType *jointAverageOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(25) ); + jointAverageOutputObject->Set(averageProbability); + + MeasurementObjectType *firstMeasureOfInformationCorrelationOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(26) ); + firstMeasureOfInformationCorrelationOutputObject->Set(firstMeasureOfInformationCorrelation); + + MeasurementObjectType *secondMeasureOfInformationCorrelationOutputObject = + static_cast< MeasurementObjectType * >( this->ProcessObject::GetOutput(27) ); + secondMeasureOfInformationCorrelationOutputObject->Set(secondMeasureOfInformationCorrelation); +} + +template< typename THistogram > +void +EnhancedHistogramToTextureFeaturesFilter< THistogram >::ComputeMeansAndVariances(double & pixelMean, + double & marginalMean, + double & marginalDevSquared, + double & pixelVariance) +{ + // This function takes two passes through the histogram and two passes through + // an array of the same length as a histogram axis. This could probably be + // cleverly compressed to one pass, but it's not clear that that's necessary. - // Initialize everything - typename HistogramType::SizeValueType binsPerAxis = inputHistogram->GetSize(0); - double *marginalSums = new double[binsPerAxis]; - for ( double *ms_It = marginalSums; + typedef typename HistogramType::ConstIterator HistogramIterator; + + const HistogramType *inputHistogram = this->GetInput(); + + // Initialize everything + typename HistogramType::SizeValueType binsPerAxis = inputHistogram->GetSize(0); + double *marginalSums = new double[binsPerAxis]; + for ( double *ms_It = marginalSums; ms_It < marginalSums + binsPerAxis; ms_It++ ) - { - *ms_It = 0; - } - pixelMean = 0; - - typename RelativeFrequencyContainerType::const_iterator rFreqIterator = - m_RelativeFrequencyContainer.begin(); - - // Ok, now do the first pass through the histogram to get the marginal sums - // and compute the pixel mean - HistogramIterator hit = inputHistogram->Begin(); - while ( hit != inputHistogram->End() ) - { - RelativeFrequencyType frequency = *rFreqIterator; - IndexType index = inputHistogram->GetIndex( hit.GetInstanceIdentifier() ); - pixelMean += index[0] * frequency; - marginalSums[index[0]] += frequency; - ++hit; - ++rFreqIterator; - } - - /* Now get the mean and deviaton of the marginal sums. + { + *ms_It = 0; + } + pixelMean = 0; + + typename RelativeFrequencyContainerType::const_iterator rFreqIterator = + m_RelativeFrequencyContainer.begin(); + + // Ok, now do the first pass through the histogram to get the marginal sums + // and compute the pixel mean + HistogramIterator hit = inputHistogram->Begin(); + while ( hit != inputHistogram->End() ) + { + RelativeFrequencyType frequency = *rFreqIterator; + IndexType index = inputHistogram->GetIndex( hit.GetInstanceIdentifier() ); + pixelMean += index[0] * frequency; + marginalSums[index[0]] += frequency; + ++hit; + ++rFreqIterator; + } + + /* Now get the mean and deviaton of the marginal sums. Compute incremental mean and SD, a la Knuth, "The Art of Computer Programming, Volume 2: Seminumerical Algorithms", section 4.2.2. Compute mean and standard deviation using the recurrence relation: M(1) = x(1), M(k) = M(k-1) + (x(k) - M(k-1) ) / k S(1) = 0, S(k) = S(k-1) + (x(k) - M(k-1)) * (x(k) - M(k)) for 2 <= k <= n, then sigma = std::sqrt(S(n) / n) (or divide by n-1 for sample SD instead of population SD). */ - marginalMean = marginalSums[0]; - marginalDevSquared = 0; - for ( unsigned int arrayIndex = 1; arrayIndex < binsPerAxis; arrayIndex++ ) - { - int k = arrayIndex + 1; - double M_k_minus_1 = marginalMean; - double S_k_minus_1 = marginalDevSquared; - double x_k = marginalSums[arrayIndex]; - - double M_k = M_k_minus_1 + ( x_k - M_k_minus_1 ) / k; - double S_k = S_k_minus_1 + ( x_k - M_k_minus_1 ) * ( x_k - M_k ); - - marginalMean = M_k; - marginalDevSquared = S_k; - } - marginalDevSquared = marginalDevSquared / binsPerAxis; - - rFreqIterator = m_RelativeFrequencyContainer.begin(); - // OK, now compute the pixel variances. - pixelVariance = 0; - for ( hit = inputHistogram->Begin(); hit != inputHistogram->End(); ++hit ) - { - RelativeFrequencyType frequency = *rFreqIterator; - IndexType index = inputHistogram->GetIndex( hit.GetInstanceIdentifier() ); - pixelVariance += ( index[0] - pixelMean ) * ( index[0] - pixelMean ) * frequency; - ++rFreqIterator; - } - - delete[] marginalSums; - } + marginalMean = marginalSums[0]; + marginalDevSquared = 0; + for ( unsigned int arrayIndex = 1; arrayIndex < binsPerAxis; arrayIndex++ ) + { + int k = arrayIndex + 1; + double M_k_minus_1 = marginalMean; + double S_k_minus_1 = marginalDevSquared; + double x_k = marginalSums[arrayIndex]; + + double M_k = M_k_minus_1 + ( x_k - M_k_minus_1 ) / k; + double S_k = S_k_minus_1 + ( x_k - M_k_minus_1 ) * ( x_k - M_k ); + + marginalMean = M_k; + marginalDevSquared = S_k; + } + marginalDevSquared = marginalDevSquared / binsPerAxis; + + rFreqIterator = m_RelativeFrequencyContainer.begin(); + // OK, now compute the pixel variances. + pixelVariance = 0; + for ( hit = inputHistogram->Begin(); hit != inputHistogram->End(); ++hit ) + { + RelativeFrequencyType frequency = *rFreqIterator; + IndexType index = inputHistogram->GetIndex( hit.GetInstanceIdentifier() ); + pixelVariance += ( index[0] - pixelMean ) * ( index[0] - pixelMean ) * frequency; + ++rFreqIterator; + } - template< typename THistogram > - const - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetEnergyOutput() const - { - return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(0) ); - } + delete[] marginalSums; +} - template< typename THistogram > - const - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetEntropyOutput() const - { - return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(1) ); - } +template< typename THistogram > +const +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetEnergyOutput() const +{ + return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(0) ); +} - template< typename THistogram > - const - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetCorrelationOutput() const - { - return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(2) ); - } +template< typename THistogram > +const +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetEntropyOutput() const +{ + return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(1) ); +} - template< typename THistogram > - const - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetInverseDifferenceMomentOutput() const - { - return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(3) ); - } +template< typename THistogram > +const +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetCorrelationOutput() const +{ + return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(2) ); +} - template< typename THistogram > - const - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetInertiaOutput() const - { - return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(4) ); - } +template< typename THistogram > +const +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetInverseDifferenceMomentOutput() const +{ + return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(3) ); +} - template< typename THistogram > - const - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetClusterShadeOutput() const - { - return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(5) ); - } +template< typename THistogram > +const +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetInertiaOutput() const +{ + return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(4) ); +} - template< typename THistogram > - const - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetClusterProminenceOutput() const - { - return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(6) ); - } +template< typename THistogram > +const +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetClusterShadeOutput() const +{ + return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(5) ); +} - template< typename THistogram > - const - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetHaralickCorrelationOutput() const - { - return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(7) ); - } +template< typename THistogram > +const +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetClusterProminenceOutput() const +{ + return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(6) ); +} - itkMacroGLCMFeature(Autocorrelation,8) - itkMacroGLCMFeature(Contrast,9) - itkMacroGLCMFeature(Dissimilarity,10) - itkMacroGLCMFeature(MaximumProbability,11) - itkMacroGLCMFeature(InverseVariance,12) - itkMacroGLCMFeature(Homogeneity1,13) - itkMacroGLCMFeature(ClusterTendency,14) - itkMacroGLCMFeature(Variance,15) - itkMacroGLCMFeature(SumAverage,16) - itkMacroGLCMFeature(SumEntropy,17) - itkMacroGLCMFeature(SumVariance,18) - itkMacroGLCMFeature(DifferenceAverage,19) - itkMacroGLCMFeature(DifferenceEntropy,20) - itkMacroGLCMFeature(DifferenceVariance,21) - itkMacroGLCMFeature(InverseDifferenceMomentNormalized,22) - itkMacroGLCMFeature(InverseDifferenceNormalized,23) - itkMacroGLCMFeature(InverseDifference,24) - - template< typename THistogram > - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetEnergy() const - { - return this->GetEnergyOutput()->Get(); - } +template< typename THistogram > +const +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementObjectType * +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetHaralickCorrelationOutput() const +{ + return static_cast< const MeasurementObjectType * >( this->ProcessObject::GetOutput(7) ); +} - template< typename THistogram > - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetEntropy() const - { - return this->GetEntropyOutput()->Get(); - } +itkMacroGLCMFeature(Autocorrelation,8) +itkMacroGLCMFeature(Contrast,9) +itkMacroGLCMFeature(Dissimilarity,10) +itkMacroGLCMFeature(MaximumProbability,11) +itkMacroGLCMFeature(InverseVariance,12) +itkMacroGLCMFeature(Homogeneity1,13) +itkMacroGLCMFeature(ClusterTendency,14) +itkMacroGLCMFeature(Variance,15) +itkMacroGLCMFeature(SumAverage,16) +itkMacroGLCMFeature(SumEntropy,17) +itkMacroGLCMFeature(SumVariance,18) +itkMacroGLCMFeature(DifferenceAverage,19) +itkMacroGLCMFeature(DifferenceEntropy,20) +itkMacroGLCMFeature(DifferenceVariance,21) +itkMacroGLCMFeature(InverseDifferenceMomentNormalized,22) +itkMacroGLCMFeature(InverseDifferenceNormalized,23) +itkMacroGLCMFeature(InverseDifference,24) +itkMacroGLCMFeature(JointAverage,25) +itkMacroGLCMFeature(FirstMeasureOfInformationCorrelation,26) +itkMacroGLCMFeature(SecondMeasureOfInformationCorrelation,27) + +template< typename THistogram > +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetEnergy() const +{ + return this->GetEnergyOutput()->Get(); +} - template< typename THistogram > - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetCorrelation() const - { - return this->GetCorrelationOutput()->Get(); - } +template< typename THistogram > +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetEntropy() const +{ + return this->GetEntropyOutput()->Get(); +} - template< typename THistogram > - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetInverseDifferenceMoment() const - { - return this->GetInverseDifferenceMomentOutput()->Get(); - } +template< typename THistogram > +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetCorrelation() const +{ + return this->GetCorrelationOutput()->Get(); +} - template< typename THistogram > - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetInertia() const - { - return this->GetInertiaOutput()->Get(); - } +template< typename THistogram > +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetInverseDifferenceMoment() const +{ + return this->GetInverseDifferenceMomentOutput()->Get(); +} - template< typename THistogram > - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetClusterShade() const - { - return this->GetClusterShadeOutput()->Get(); - } +template< typename THistogram > +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetInertia() const +{ + return this->GetInertiaOutput()->Get(); +} - template< typename THistogram > - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetClusterProminence() const - { - return this->GetClusterProminenceOutput()->Get(); - } +template< typename THistogram > +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetClusterShade() const +{ + return this->GetClusterShadeOutput()->Get(); +} - template< typename THistogram > - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetHaralickCorrelation() const - { - return this->GetHaralickCorrelationOutput()->Get(); - } +template< typename THistogram > +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetClusterProminence() const +{ + return this->GetClusterProminenceOutput()->Get(); +} + +template< typename THistogram > +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetHaralickCorrelation() const +{ + return this->GetHaralickCorrelationOutput()->Get(); +} #define itkMacroGLCMFeatureSwitch(name) \ - case name : \ - return this->Get##name() - template< typename THistogram > - typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::GetFeature(TextureFeatureName feature) - { - switch ( feature ) - { - itkMacroGLCMFeatureSwitch(Energy); - itkMacroGLCMFeatureSwitch(Entropy); - itkMacroGLCMFeatureSwitch(Correlation); - itkMacroGLCMFeatureSwitch(InverseDifferenceMoment); - itkMacroGLCMFeatureSwitch(Inertia); - itkMacroGLCMFeatureSwitch(ClusterShade); - itkMacroGLCMFeatureSwitch(ClusterProminence); - itkMacroGLCMFeatureSwitch(HaralickCorrelation); - itkMacroGLCMFeatureSwitch(Autocorrelation); - itkMacroGLCMFeatureSwitch(Contrast); - itkMacroGLCMFeatureSwitch(Dissimilarity); - itkMacroGLCMFeatureSwitch(MaximumProbability); - itkMacroGLCMFeatureSwitch(InverseVariance); - itkMacroGLCMFeatureSwitch(Homogeneity1); - itkMacroGLCMFeatureSwitch(ClusterTendency); - itkMacroGLCMFeatureSwitch(Variance); - itkMacroGLCMFeatureSwitch(SumAverage); - itkMacroGLCMFeatureSwitch(SumEntropy); - itkMacroGLCMFeatureSwitch(SumVariance); - itkMacroGLCMFeatureSwitch(DifferenceAverage); - itkMacroGLCMFeatureSwitch(DifferenceEntropy); - itkMacroGLCMFeatureSwitch(DifferenceVariance); - itkMacroGLCMFeatureSwitch(InverseDifferenceMomentNormalized); - itkMacroGLCMFeatureSwitch(InverseDifferenceNormalized); - itkMacroGLCMFeatureSwitch(InverseDifference); - default: - return 0; - } - } + case name : \ + return this->Get##name() +template< typename THistogram > +typename EnhancedHistogramToTextureFeaturesFilter< THistogram >::MeasurementType +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::GetFeature(TextureFeatureName feature) +{ + switch ( feature ) + { + itkMacroGLCMFeatureSwitch(Energy); + itkMacroGLCMFeatureSwitch(Entropy); + itkMacroGLCMFeatureSwitch(Correlation); + itkMacroGLCMFeatureSwitch(InverseDifferenceMoment); + itkMacroGLCMFeatureSwitch(Inertia); + itkMacroGLCMFeatureSwitch(ClusterShade); + itkMacroGLCMFeatureSwitch(ClusterProminence); + itkMacroGLCMFeatureSwitch(HaralickCorrelation); + itkMacroGLCMFeatureSwitch(Autocorrelation); + itkMacroGLCMFeatureSwitch(Contrast); + itkMacroGLCMFeatureSwitch(Dissimilarity); + itkMacroGLCMFeatureSwitch(MaximumProbability); + itkMacroGLCMFeatureSwitch(InverseVariance); + itkMacroGLCMFeatureSwitch(Homogeneity1); + itkMacroGLCMFeatureSwitch(ClusterTendency); + itkMacroGLCMFeatureSwitch(Variance); + itkMacroGLCMFeatureSwitch(SumAverage); + itkMacroGLCMFeatureSwitch(SumEntropy); + itkMacroGLCMFeatureSwitch(SumVariance); + itkMacroGLCMFeatureSwitch(DifferenceAverage); + itkMacroGLCMFeatureSwitch(DifferenceEntropy); + itkMacroGLCMFeatureSwitch(DifferenceVariance); + itkMacroGLCMFeatureSwitch(InverseDifferenceMomentNormalized); + itkMacroGLCMFeatureSwitch(InverseDifferenceNormalized); + itkMacroGLCMFeatureSwitch(InverseDifference); + itkMacroGLCMFeatureSwitch(JointAverage); + itkMacroGLCMFeatureSwitch(FirstMeasureOfInformationCorrelation); + itkMacroGLCMFeatureSwitch(SecondMeasureOfInformationCorrelation); + default: + return 0; + } +} #undef itkMacroGLCMFeatureSwitch - template< typename THistogram > - void - EnhancedHistogramToTextureFeaturesFilter< THistogram > - ::PrintSelf(std::ostream & os, Indent indent) const - { - Superclass::PrintSelf(os, indent); - } - } // end of namespace Statistics +template< typename THistogram > +void +EnhancedHistogramToTextureFeaturesFilter< THistogram > +::PrintSelf(std::ostream & os, Indent indent) const +{ + Superclass::PrintSelf(os, indent); +} +} // end of namespace Statistics } // end of namespace itk -#endif \ No newline at end of file +#endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter.h similarity index 79% copy from Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.h copy to Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter.h index 0c7392744b..29c801ebd6 100644 --- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.h +++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter.h @@ -1,245 +1,245 @@ /*=================================================================== 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. ===================================================================*/ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ -#ifndef __itkEnhancedScalarImageToRunLengthFeaturesFilter_h -#define __itkEnhancedScalarImageToRunLengthFeaturesFilter_h +#ifndef __itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter_h +#define __itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter_h #include "itkDataObjectDecorator.h" -#include "itkEnhancedHistogramToRunLengthFeaturesFilter.h" -#include "itkEnhancedScalarImageToRunLengthMatrixFilter.h" +#include "itkEnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter.h" +#include "itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.h" namespace itk { namespace Statistics { - /** \class EnhancedScalarImageToRunLengthFeaturesFilter + /** \class EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter * \brief This class computes run length descriptions from an image. * * By default, run length features are computed for each spatial * direction and then averaged afterward, so it is possible to access the * standard deviations of the texture features. These values give a clue as * to texture anisotropy. However, doing this is much more work, because it * involved computing one for each offset given. To compute a single * matrix using the first offset, call FastCalculationsOn(). If this is called, * then the texture standard deviations will not be computed (and will be set * to zero), but texture computation will be much faster. * * This class is templated over the input image type. * * Template Parameters: * The image type, and the type of histogram frequency container. If you are * using a large number of bins per axis, a sparse frequency container may be * advisable. The default is to use a dense frequency container. * * Inputs and parameters: * -# An image * -# A mask defining the region over which texture features will be * calculated. (Optional) * -# The pixel value that defines the "inside" of the mask. (Optional, defaults * to 1 if a mask is set.) * -# The set of features to be calculated. These features are defined - * in the HistogramToRunLengthFeaturesFilter class. + * in the HistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter class. * -# The number of intensity bins. (Optional, defaults to 256.) * -# The set of directions (offsets) to average across. (Optional, defaults to * {(-1, 0), (-1, -1), (0, -1), (1, -1)} for 2D images and scales analogously * for ND images.) * -# The pixel intensity range over which the features will be calculated. * (Optional, defaults to the full dynamic range of the pixel type.) * -# The distance range over which the features will be calculated. * (Optional, defaults to the full dynamic range of double type.) * * In general, the default parameter values should be sufficient. * * Outputs: * (1) The average value of each feature. * (2) The standard deviation in the values of each feature. * * Print references: * M. M. Galloway. Texture analysis using gray level run lengths. Computer * Graphics and Image Processing, 4:172-179, 1975. * * A. Chu, C. M. Sehgal, and J. F. Greenleaf. Use of gray value distribution of * run lengths for texture analysis. Pattern Recognition Letters, 11:415-420, * 1990. * * B. R. Dasarathy and E. B. Holder. Image characterizations based on joint * gray-level run-length distributions. Pattern Recognition Letters, 12:490-502, * 1991. * * IJ article: http://hdl.handle.net/1926/1374 * - * \sa EnhancedScalarImageToRunLengthFeaturesFilter - * \sa ScalarImageToRunLengthMatrixFilter - * \sa HistogramToRunLengthFeaturesFilter + * \sa EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter + * \sa ScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter + * \sa HistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter * * \author: Nick Tustison * \ingroup ITKStatistics */ template< typename TImageType, typename THistogramFrequencyContainer = DenseFrequencyContainer2 > - class EnhancedScalarImageToRunLengthFeaturesFilter:public ProcessObject + class EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter:public ProcessObject { public: /** Standard typedefs */ - typedef EnhancedScalarImageToRunLengthFeaturesFilter Self; + typedef EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter Self; typedef ProcessObject Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; /** Run-time type information (and related methods). */ - itkTypeMacro(EnhancedScalarImageToRunLengthFeaturesFilter, ProcessObject); + itkTypeMacro(EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter, ProcessObject); /** standard New() method support */ itkNewMacro(Self); typedef THistogramFrequencyContainer FrequencyContainerType; typedef TImageType ImageType; typedef typename ImageType::Pointer ImagePointer; typedef typename ImageType::PixelType PixelType; typedef typename ImageType::OffsetType OffsetType; typedef VectorContainer< unsigned char, OffsetType > OffsetVector; typedef typename OffsetVector::Pointer OffsetVectorPointer; typedef typename OffsetVector::ConstPointer OffsetVectorConstPointer; - typedef EnhancedScalarImageToRunLengthMatrixFilter< - ImageType, FrequencyContainerType > RunLengthMatrixFilterType; + typedef EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter< + ImageType, FrequencyContainerType > NeighbourhoodGreyLevelDifferenceMatrixFilterType; - typedef typename RunLengthMatrixFilterType::HistogramType + typedef typename NeighbourhoodGreyLevelDifferenceMatrixFilterType::HistogramType HistogramType; - typedef EnhancedHistogramToRunLengthFeaturesFilter< HistogramType > - RunLengthFeaturesFilterType; + typedef EnhancedHistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter< HistogramType > + NeighbourhoodGreyLevelDifferenceFeaturesFilterType; - typedef short RunLengthFeatureName; + typedef short NeighbourhoodGreyLevelDifferenceFeatureName; typedef VectorContainer FeatureNameVector; + NeighbourhoodGreyLevelDifferenceFeatureName> FeatureNameVector; typedef typename FeatureNameVector::Pointer FeatureNameVectorPointer; typedef typename FeatureNameVector::ConstPointer FeatureNameVectorConstPointer; typedef VectorContainer< unsigned char, double > FeatureValueVector; typedef typename FeatureValueVector::Pointer FeatureValueVectorPointer; /** Smart Pointer type to a DataObject. */ typedef DataObject::Pointer DataObjectPointer; /** Type of DataObjects used for scalar outputs */ typedef DataObjectDecorator< FeatureValueVector > FeatureValueVectorDataObjectType; const FeatureValueVectorDataObjectType * GetFeatureMeansOutput() const; const FeatureValueVectorDataObjectType * GetFeatureStandardDeviationsOutput() const; /** Connects the input image for which the features are going to be computed */ using Superclass::SetInput; void SetInput(const ImageType *); const ImageType * GetInput() const; /** Return the feature means and deviations. */ itkGetConstReferenceObjectMacro(FeatureMeans, FeatureValueVector); itkGetConstReferenceObjectMacro(FeatureStandardDeviations, FeatureValueVector); /** Set the desired feature set. Optional, for default value see above. */ itkSetConstObjectMacro(RequestedFeatures, FeatureNameVector); itkGetConstObjectMacro(RequestedFeatures, FeatureNameVector); /** Set the offsets over which the co-occurrence pairs will be computed. Optional; for default value see above. */ itkSetConstObjectMacro(Offsets, OffsetVector); itkGetConstObjectMacro(Offsets, OffsetVector); /** Set number of histogram bins along each axis. Optional; for default value see above. */ void SetNumberOfBinsPerAxis(unsigned int); /** Set the min and max (inclusive) pixel value that will be used for feature calculations. Optional; for default value see above. */ void SetPixelValueMinMax(PixelType min, PixelType max); /** Set the min and max (inclusive) pixel value that will be used for feature calculations. Optional; for default value see above. */ void SetDistanceValueMinMax( double min, double max ); /** Connects the mask image for which the histogram is going to be computed. Optional; for default value see above. */ void SetMaskImage(const ImageType *); const ImageType * GetMaskImage() const; /** Set the pixel value of the mask that should be considered "inside" the object. Optional; for default value see above. */ void SetInsidePixelValue(PixelType InsidePixelValue); itkGetConstMacro(FastCalculations, bool); itkSetMacro(FastCalculations, bool); itkBooleanMacro(FastCalculations); protected: - EnhancedScalarImageToRunLengthFeaturesFilter(); - ~EnhancedScalarImageToRunLengthFeaturesFilter() override {} - void PrintSelf( std::ostream & os, Indent indent ) const ITK_OVERRIDE; + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter(); + virtual ~EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter() {} + virtual void PrintSelf( std::ostream & os, Indent indent ) const ITK_OVERRIDE; void FastCompute(); void FullCompute(); /** This method causes the filter to generate its output. */ - void GenerateData() ITK_OVERRIDE; + virtual void GenerateData() ITK_OVERRIDE; /** Make a DataObject to be used for output output. */ typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType; using Superclass::MakeOutput; - DataObjectPointer MakeOutput(DataObjectPointerArraySizeType) ITK_OVERRIDE; + virtual DataObjectPointer MakeOutput(DataObjectPointerArraySizeType) ITK_OVERRIDE; private: - typename RunLengthMatrixFilterType::Pointer m_RunLengthMatrixGenerator; + typename NeighbourhoodGreyLevelDifferenceMatrixFilterType::Pointer m_NeighbourhoodGreyLevelDifferenceMatrixGenerator; FeatureValueVectorPointer m_FeatureMeans; FeatureValueVectorPointer m_FeatureStandardDeviations; FeatureNameVectorConstPointer m_RequestedFeatures; OffsetVectorConstPointer m_Offsets; bool m_FastCalculations; }; } // end of namespace Statistics } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION -#include "itkEnhancedScalarImageToRunLengthFeaturesFilter.hxx" +#include "itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter.hxx" #endif #endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.hxx b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter.hxx similarity index 63% copy from Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.hxx copy to Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter.hxx index bda0fccfc2..eaf309549c 100644 --- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.hxx +++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter.hxx @@ -1,410 +1,410 @@ /*=================================================================== 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. ===================================================================*/ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ -#ifndef __itkEnhancedScalarImageToRunLengthFeaturesFilter_hxx -#define __itkEnhancedScalarImageToRunLengthFeaturesFilter_hxx +#ifndef __itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter_hxx +#define __itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter_hxx -#include "itkEnhancedScalarImageToRunLengthFeaturesFilter.h" +#include "itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter.h" #include "itkNeighborhood.h" #include #include "vnl/vnl_math.h" namespace itk { namespace Statistics { template - EnhancedScalarImageToRunLengthFeaturesFilter - ::EnhancedScalarImageToRunLengthFeaturesFilter() + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter + ::EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter() { this->SetNumberOfRequiredInputs( 1 ); this->SetNumberOfRequiredOutputs( 1 ); for( int i = 0; i < 2; ++i ) { this->ProcessObject::SetNthOutput( i, this->MakeOutput( i ) ); } - this->m_RunLengthMatrixGenerator = RunLengthMatrixFilterType::New(); + this->m_NeighbourhoodGreyLevelDifferenceMatrixGenerator = NeighbourhoodGreyLevelDifferenceMatrixFilterType::New(); this->m_FeatureMeans = FeatureValueVector::New(); this->m_FeatureStandardDeviations = FeatureValueVector::New(); // Set the requested features to the default value: // {Energy, Entropy, InverseDifferenceMoment, Inertia, ClusterShade, // ClusterProminence} FeatureNameVectorPointer requestedFeatures = FeatureNameVector::New(); // can't directly set this->m_RequestedFeatures since it is const! - requestedFeatures->push_back( RunLengthFeaturesFilterType::ShortRunEmphasis ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::LongRunEmphasis ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::GreyLevelNonuniformity ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::RunLengthNonuniformity ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::LowGreyLevelRunEmphasis ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::HighGreyLevelRunEmphasis ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::ShortRunLowGreyLevelEmphasis ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::ShortRunHighGreyLevelEmphasis ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::LongRunLowGreyLevelEmphasis ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::LongRunHighGreyLevelEmphasis ); + requestedFeatures->push_back( NeighbourhoodGreyLevelDifferenceFeaturesFilterType::Coarseness ); + requestedFeatures->push_back( NeighbourhoodGreyLevelDifferenceFeaturesFilterType::Contrast ); + requestedFeatures->push_back( NeighbourhoodGreyLevelDifferenceFeaturesFilterType::Busyness ); + requestedFeatures->push_back( NeighbourhoodGreyLevelDifferenceFeaturesFilterType::Complexity ); + requestedFeatures->push_back( NeighbourhoodGreyLevelDifferenceFeaturesFilterType::Strength ); requestedFeatures->push_back( 20 ); this->SetRequestedFeatures( requestedFeatures ); // Set the offset directions to their defaults: half of all the possible // directions 1 pixel away. (The other half is included by symmetry.) // We use a neighborhood iterator to calculate the appropriate offsets. typedef Neighborhood NeighborhoodType; NeighborhoodType hood; hood.SetRadius( 1 ); // select all "previous" neighbors that are face+edge+vertex // connected to the current pixel. do not include the center pixel. unsigned int centerIndex = hood.GetCenterNeighborhoodIndex(); OffsetVectorPointer offsets = OffsetVector::New(); for( unsigned int d = 0; d < centerIndex; d++ ) { OffsetType offset = hood.GetOffset( d ); offsets->push_back( offset ); } this->SetOffsets( offsets ); this->m_FastCalculations = false; } template typename - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::DataObjectPointer - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::MakeOutput( DataObjectPointerArraySizeType itkNotUsed(idx) ) { return FeatureValueVectorDataObjectType::New().GetPointer(); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::GenerateData(void) { if ( this->m_FastCalculations ) { this->FastCompute(); } else { this->FullCompute(); } } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::FullCompute() { - int numOffsets = this->m_Offsets->size(); int numFeatures = this->m_RequestedFeatures->size(); - double **features; - - features = new double *[numOffsets]; - for( int i = 0; i < numOffsets; i++ ) - { - features[i] = new double[numFeatures]; - } + double *features = new double [numFeatures]; unsigned long numberOfVoxels = 0; ImageRegionConstIterator voxelCountIter(this->GetMaskImage(),this->GetMaskImage()->GetLargestPossibleRegion()); while ( ! voxelCountIter.IsAtEnd() ) { if (voxelCountIter.Get() > 0) ++numberOfVoxels; ++voxelCountIter; } // For each offset, calculate each feature typename OffsetVector::ConstIterator offsetIt; int offsetNum, featureNum; - typedef typename RunLengthFeaturesFilterType::RunLengthFeatureName - InternalRunLengthFeatureName; + typedef typename NeighbourhoodGreyLevelDifferenceFeaturesFilterType::NeighbourhoodGreyLevelDifferenceFeatureName + InternalNeighbourhoodGreyLevelDifferenceFeatureName; + std::vector tVector; for( offsetIt = this->m_Offsets->Begin(), offsetNum = 0; offsetIt != this->m_Offsets->End(); offsetIt++, offsetNum++ ) { - this->m_RunLengthMatrixGenerator->SetOffset( offsetIt.Value() ); - this->m_RunLengthMatrixGenerator->Update(); - typename RunLengthFeaturesFilterType::Pointer runLengthMatrixCalculator = - RunLengthFeaturesFilterType::New(); - runLengthMatrixCalculator->SetInput( - this->m_RunLengthMatrixGenerator->GetOutput() ); - runLengthMatrixCalculator->SetNumberOfVoxels(numberOfVoxels); - runLengthMatrixCalculator->Update(); + MITK_INFO << "Adding offset " << offsetIt.Value(); + tVector.push_back(offsetIt.Value()); + } + this->m_NeighbourhoodGreyLevelDifferenceMatrixGenerator->AddOffsets( tVector); + this->m_NeighbourhoodGreyLevelDifferenceMatrixGenerator->Update(); + typename NeighbourhoodGreyLevelDifferenceFeaturesFilterType::Pointer NeighbourhoodGreyLevelDifferenceMatrixCalculator = + NeighbourhoodGreyLevelDifferenceFeaturesFilterType::New(); + NeighbourhoodGreyLevelDifferenceMatrixCalculator->SetInput( + this->m_NeighbourhoodGreyLevelDifferenceMatrixGenerator->GetOutput() ); + NeighbourhoodGreyLevelDifferenceMatrixCalculator->SetSiMatrix( + this->m_NeighbourhoodGreyLevelDifferenceMatrixGenerator->GetSiMatrix() ); + NeighbourhoodGreyLevelDifferenceMatrixCalculator->SetNumberOfVoxels(numberOfVoxels); + NeighbourhoodGreyLevelDifferenceMatrixCalculator->Update(); typename FeatureNameVector::ConstIterator fnameIt; for( fnameIt = this->m_RequestedFeatures->Begin(), featureNum = 0; fnameIt != this->m_RequestedFeatures->End(); fnameIt++, featureNum++ ) { - features[offsetNum][featureNum] = runLengthMatrixCalculator->GetFeature( - ( InternalRunLengthFeatureName )fnameIt.Value() ); + InternalNeighbourhoodGreyLevelDifferenceFeatureName tn = ( InternalNeighbourhoodGreyLevelDifferenceFeatureName )fnameIt.Value(); + double xx = NeighbourhoodGreyLevelDifferenceMatrixCalculator->GetFeature(tn); + + features[featureNum] = xx; } - } + // Now get the mean and deviaton of each feature across the offsets. this->m_FeatureMeans->clear(); this->m_FeatureStandardDeviations->clear(); double *tempFeatureMeans = new double[numFeatures]; double *tempFeatureDevs = new double[numFeatures]; /*Compute incremental mean and SD, a la Knuth, "The Art of Computer Programming, Volume 2: Seminumerical Algorithms", section 4.2.2. Compute mean and standard deviation using the recurrence relation: M(1) = x(1), M(k) = M(k-1) + (x(k) - M(k-1) ) / k S(1) = 0, S(k) = S(k-1) + (x(k) - M(k-1)) * (x(k) - M(k)) for 2 <= k <= n, then sigma = std::sqrt(S(n) / n) (or divide by n-1 for sample SD instead of population SD). */ // Set up the initial conditions (k = 1) for( featureNum = 0; featureNum < numFeatures; featureNum++ ) { - tempFeatureMeans[featureNum] = features[0][featureNum]; - tempFeatureDevs[featureNum] = 0; + this->m_FeatureMeans->push_back( features[featureNum] ); + //tempFeatureMeans[featureNum] = features[0][featureNum]; + //tempFeatureDevs[featureNum] = 0; } - // Run through the recurrence (k = 2 ... N) - for( offsetNum = 1; offsetNum < numOffsets; offsetNum++ ) + // Zone through the recurrence (k = 2 ... N) + /*for( offsetNum = 1; offsetNum < numOffsets; offsetNum++ ) { int k = offsetNum + 1; for( featureNum = 0; featureNum < numFeatures; featureNum++ ) { double M_k_minus_1 = tempFeatureMeans[featureNum]; double S_k_minus_1 = tempFeatureDevs[featureNum]; double x_k = features[offsetNum][featureNum]; double M_k = M_k_minus_1 + ( x_k - M_k_minus_1 ) / k; double S_k = S_k_minus_1 + ( x_k - M_k_minus_1 ) * ( x_k - M_k ); tempFeatureMeans[featureNum] = M_k; tempFeatureDevs[featureNum] = S_k; } } for( featureNum = 0; featureNum < numFeatures; featureNum++ ) { tempFeatureDevs[featureNum] = std::sqrt( tempFeatureDevs[featureNum] / numOffsets ); this->m_FeatureMeans->push_back( tempFeatureMeans[featureNum] ); this->m_FeatureStandardDeviations->push_back( tempFeatureDevs[featureNum] ); } + */ FeatureValueVectorDataObjectType *meanOutputObject = itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 0 ) ); meanOutputObject->Set( this->m_FeatureMeans ); FeatureValueVectorDataObjectType *standardDeviationOutputObject = itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 1 ) ); standardDeviationOutputObject->Set( this->m_FeatureStandardDeviations ); delete[] tempFeatureMeans; delete[] tempFeatureDevs; - for( int i = 0; i < numOffsets; i++ ) + /*for( int i = 0; i < numOffsets; i++ ) { delete[] features[i]; - } + }*/ delete[] features; } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::FastCompute() { // Compute the feature for the first offset typename OffsetVector::ConstIterator offsetIt = this->m_Offsets->Begin(); - this->m_RunLengthMatrixGenerator->SetOffset( offsetIt.Value() ); - - this->m_RunLengthMatrixGenerator->Update(); - typename RunLengthFeaturesFilterType::Pointer runLengthMatrixCalculator = - RunLengthFeaturesFilterType::New(); - runLengthMatrixCalculator->SetInput( - this->m_RunLengthMatrixGenerator->GetOutput() ); - runLengthMatrixCalculator->Update(); - - typedef typename RunLengthFeaturesFilterType::RunLengthFeatureName - InternalRunLengthFeatureName; + this->m_NeighbourhoodGreyLevelDifferenceMatrixGenerator->SetOffset( offsetIt.Value() ); + + this->m_NeighbourhoodGreyLevelDifferenceMatrixGenerator->Update(); + typename NeighbourhoodGreyLevelDifferenceFeaturesFilterType::Pointer NeighbourhoodGreyLevelDifferenceMatrixCalculator = + NeighbourhoodGreyLevelDifferenceFeaturesFilterType::New(); + NeighbourhoodGreyLevelDifferenceMatrixCalculator->SetInput( + this->m_NeighbourhoodGreyLevelDifferenceMatrixGenerator->GetOutput() ); + NeighbourhoodGreyLevelDifferenceMatrixCalculator->SetSiMatrix( + this->m_NeighbourhoodGreyLevelDifferenceMatrixGenerator->GetSiMatrix() ); + NeighbourhoodGreyLevelDifferenceMatrixCalculator->Update(); + + typedef typename NeighbourhoodGreyLevelDifferenceFeaturesFilterType::NeighbourhoodGreyLevelDifferenceFeatureName + InternalNeighbourhoodGreyLevelDifferenceFeatureName; this->m_FeatureMeans->clear(); this->m_FeatureStandardDeviations->clear(); typename FeatureNameVector::ConstIterator fnameIt; for( fnameIt = this->m_RequestedFeatures->Begin(); fnameIt != this->m_RequestedFeatures->End(); fnameIt++ ) { - this->m_FeatureMeans->push_back( runLengthMatrixCalculator->GetFeature( - ( InternalRunLengthFeatureName )fnameIt.Value() ) ); + this->m_FeatureMeans->push_back( NeighbourhoodGreyLevelDifferenceMatrixCalculator->GetFeature( + ( InternalNeighbourhoodGreyLevelDifferenceFeatureName )fnameIt.Value() ) ); this->m_FeatureStandardDeviations->push_back( 0.0 ); } FeatureValueVectorDataObjectType *meanOutputObject = itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 0 ) ); meanOutputObject->Set( this->m_FeatureMeans ); FeatureValueVectorDataObjectType *standardDeviationOutputObject = itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 1 ) ); standardDeviationOutputObject->Set( this->m_FeatureStandardDeviations ); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::SetInput( const ImageType *image ) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput( 0, const_cast( image ) ); - this->m_RunLengthMatrixGenerator->SetInput( image ); + this->m_NeighbourhoodGreyLevelDifferenceMatrixGenerator->SetInput( image ); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::SetNumberOfBinsPerAxis( unsigned int numberOfBins ) { itkDebugMacro( "setting NumberOfBinsPerAxis to " << numberOfBins ); - this->m_RunLengthMatrixGenerator->SetNumberOfBinsPerAxis( numberOfBins ); + this->m_NeighbourhoodGreyLevelDifferenceMatrixGenerator->SetNumberOfBinsPerAxis( numberOfBins ); this->Modified(); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::SetPixelValueMinMax( PixelType min, PixelType max ) { itkDebugMacro( "setting Min to " << min << "and Max to " << max ); - this->m_RunLengthMatrixGenerator->SetPixelValueMinMax( min, max ); + this->m_NeighbourhoodGreyLevelDifferenceMatrixGenerator->SetPixelValueMinMax( min, max ); this->Modified(); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::SetDistanceValueMinMax( double min, double max ) { itkDebugMacro( "setting Min to " << min << "and Max to " << max ); - this->m_RunLengthMatrixGenerator->SetDistanceValueMinMax( min, max ); + this->m_NeighbourhoodGreyLevelDifferenceMatrixGenerator->SetDistanceValueMinMax( min, max ); this->Modified(); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::SetMaskImage( const ImageType *image ) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput( 1, const_cast< ImageType * >( image ) ); - this->m_RunLengthMatrixGenerator->SetMaskImage( image ); + this->m_NeighbourhoodGreyLevelDifferenceMatrixGenerator->SetMaskImage( image ); } template const TImage * - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::GetInput() const { if ( this->GetNumberOfInputs() < 1 ) { return ITK_NULLPTR; } return static_cast( this->ProcessObject::GetInput( 0 ) ); } template const typename - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::FeatureValueVectorDataObjectType * - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::GetFeatureMeansOutput() const { return itkDynamicCastInDebugMode (this->ProcessObject::GetOutput( 0 ) ); } template const typename - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::FeatureValueVectorDataObjectType * - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::GetFeatureStandardDeviationsOutput() const { return itkDynamicCastInDebugMode< const FeatureValueVectorDataObjectType * > ( this->ProcessObject::GetOutput( 1 ) ); } template const TImage * - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::GetMaskImage() const { if ( this->GetNumberOfInputs() < 2 ) { return ITK_NULLPTR; } return static_cast< const ImageType *>( this->ProcessObject::GetInput( 1 ) ); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::SetInsidePixelValue( PixelType insidePixelValue ) { itkDebugMacro( "setting InsidePixelValue to " << insidePixelValue ); - this->m_RunLengthMatrixGenerator->SetInsidePixelValue( insidePixelValue ); + this->m_NeighbourhoodGreyLevelDifferenceMatrixGenerator->SetInsidePixelValue( insidePixelValue ); this->Modified(); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "RequestedFeatures: " << this->GetRequestedFeatures() << std::endl; os << indent << "FeatureStandardDeviations: " << this->GetFeatureStandardDeviations() << std::endl; os << indent << "FastCalculations: " << this->GetFastCalculations() << std::endl; os << indent << "Offsets: " << this->GetOffsets() << std::endl; os << indent << "FeatureMeans: " << this->GetFeatureMeans() << std::endl; } } // end of namespace Statistics } // end of namespace itk -#endif \ No newline at end of file +#endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.h new file mode 100644 index 0000000000..697f13028f --- /dev/null +++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.h @@ -0,0 +1,299 @@ +/*=================================================================== + +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. + +===================================================================*/ + +/*========================================================================= +* +* Copyright Insight Software Consortium +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0.txt +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*=========================================================================*/ +#ifndef __itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter_h +#define __itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter_h + +#include "itkImage.h" +#include "itkHistogram.h" +#include "itkNumericTraits.h" +#include "itkVectorContainer.h" + +namespace itk +{ + namespace Statistics + { + /** \class EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter + * \brief This class computes a run length matrix (histogram) from + * a given image and a mask image if provided. Run length matrces are + * used for image texture description. + * + * This filters creates a grey-level run length matrix from a N-D scalar + * image. This is another possible texture description. See the following + * references. + * M. M. Galloway. Texture analysis using gray level run lengths. Computer + * Graphics and Image Processing, 4:172-179, 1975. + * + * A. Chu, C. M. Sehgal, and J. F. Greenleaf. Use of gray value distribution of + * run lengths for texture analysis. Pattern Recognition Letters, 11:415-420, + * 1990. + * + * B. R. Dasarathy and E. B. Holder. Image characterizations based on joint + * gray-level run-length distributions. Pattern Recognition Letters, 12:490-502, + * 1991. + * + * The basic idea is as follows: + * Given an image and an offset (e.g. (1, -1) for a 2-d image), each element + * in the joint histogram describes the frequency for a particular distance/ + * intensity pair within a given image. This distance/intensity pair can be + * described as follows: we start at a given voxel which has some intensity. + * We then "jump" to neighboring pixels in increments provided by the offset(s) + * as long as the pixel to which we are jumping is within the same intensity + * bin as the original voxel. The distance component is given by the distance + * from the original to the final voxel satisfying our jumping criteria. + * + * The offset (or offsets) along which the co-occurences are calculated can be + * set by the user. Traditionally, only one offset is used per histogram, and + * offset components in the range [-1, 1] are used. For rotation-invariant + * features averages of features computed over several histograms with different + * offsets are generally used, instead of computing features from one histogram + * create with several offsets. Additionally, instead of using offsets of two or + * more pixels in any direction, multi-resolution techniques (e.g. image + * pyramids) are generally used to deal with texture at different spatial + * resolutions. + * + * This class calculates a 2-d histogram of all the intensity/distance pairs in + * the given image's requested region, for a given set of offsets. That is, if + * a given offset falls outside of the requested region (or outside the mask) + * at a particular point, that distance/intensity pair will not be added to + * the matrix. + * + * The number of histogram bins on each axis can be set (defaults to 256). Also, + * by default the histogram min and max corresponds to the largest and smallest + * possible pixel value of that pixel type. To customize the histogram bounds + * for a given image, the max and min pixel values that will be placed in the + * histogram can be set manually. NB: The min and max are INCLUSIVE. + * + * Further, the type of histogram frequency container used is an optional + * template parameter. By default, a dense container is used, but for images + * with little texture or in cases where the user wants more histogram bins, + * a sparse container can be used for the histogram instead. + * + * WARNING: This probably won't work for pixels of double or long-double type + * unless you set the histogram min and max manually. This is because the largest + * histogram bin by default has max value of the largest possible pixel value + * plus 1. For double and long-double types, whose "RealType" as defined by the + * NumericTraits class is the same, and thus cannot hold any larger values, + * this would cause a float overflow. + * + * IJ article: http://hdl.handle.net/1926/1374 + * + * \sa ScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter + * \sa EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter + * \sa HistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter + * + * \author: Nick Tustison + * \ingroup ITKStatistics + */ + + template + class EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter : public ProcessObject + { + public: + /** Standard typedefs */ + typedef EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter Self; + typedef ProcessObject Superclass; + typedef SmartPointer Pointer; + typedef SmartPointer ConstPointer; + + /** Run-time type information (and related methods). */ + itkTypeMacro( EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter, ProcessObject ); + + /** standard New() method support */ + itkNewMacro( Self ); + + typedef TImageType ImageType; + typedef typename ImageType::Pointer ImagePointer; + typedef typename ImageType::ConstPointer ImageConstPointer; + typedef typename ImageType::PixelType PixelType; + typedef typename ImageType::IndexType IndexType; + typedef typename ImageType::RegionType RegionType; + typedef typename ImageType::SizeType RadiusType; + typedef typename ImageType::OffsetType OffsetType; + typedef VectorContainer OffsetVector; + typedef typename OffsetVector::Pointer OffsetVectorPointer; + typedef typename ImageType::PointType PointType; + + typedef typename NumericTraits::RealType MeasurementType; + typedef typename NumericTraits::RealType RealType; + + typedef Histogram + HistogramType; + typedef typename HistogramType::Pointer HistogramPointer; + typedef typename HistogramType::ConstPointer HistogramConstPointer; + typedef typename HistogramType::MeasurementVectorType MeasurementVectorType; + + /** ImageDimension constants */ + itkStaticConstMacro( ImageDimension, unsigned int, + TImageType::ImageDimension ); + + /** Specify the default number of bins per axis */ + itkStaticConstMacro( DefaultBinsPerAxis, unsigned int, 256 ); + + /** + * Set the offsets over which the intensity/distance pairs will be computed. + * Invoking this function clears the previous offsets. + * Note: for each individual offset in the OffsetVector, the rightmost non-zero + * offset element must be positive. For example, in the offset list of a 2D image, + * (1, 0) means the offset along x-axis. (1, 0) has to be set instead + * of (-1, 0). This is required from the iterating order of pixel iterator. + * + */ + itkSetObjectMacro( Offsets, OffsetVector ); + + /** + * Set offset over which the intensity/distance pairs will be computed. + * Invoking this function clears the previous offset(s). + * Note: for each individual offset, the rightmost non-zero + * offset element must be positive. For example, in the offset list of a 2D image, + * (1, 0) means the offset along x-axis. (1, 0) has to be set instead + * of (-1, 0). This is required from the iterating order of pixel iterator. + * + */ + void SetOffset( const OffsetType offset ); + + void AddOffsets( const std::vector offset ); + + /** + * Get the current offset(s). + */ + itkGetModifiableObjectMacro(Offsets, OffsetVector ); + + /** Set number of histogram bins along each axis */ + itkSetMacro( NumberOfBinsPerAxis, unsigned int ); + + /** Get number of histogram bins along each axis */ + itkGetConstMacro( NumberOfBinsPerAxis, unsigned int ); + + /** + * Set the min and max (inclusive) pixel value that will be used in + * generating the histogram. + */ + void SetPixelValueMinMax( PixelType min, PixelType max ); + + /** Get the min pixel value defining one dimension of the joint histogram. */ + itkGetConstMacro( Min, PixelType ); + + /** Get the max pixel value defining one dimension of the joint histogram. */ + itkGetConstMacro( Max, PixelType ); + + /** + * Set the min and max (inclusive) pixel value that will be used in + * generating the histogram. + */ + void SetDistanceValueMinMax( RealType min, RealType max ); + + /** + * Get the min distance value defining one dimension of the joint histogram. + */ + itkGetConstMacro( MinDistance, RealType ); + + /** + * Get the max distance value defining one dimension of the joint histogram. + */ + itkGetConstMacro( MaxDistance, RealType ); + + /** Method to set the input image */ + using Superclass::SetInput; + void SetInput( const ImageType *image ); + + /** Method to get the input image */ + const ImageType * GetInput() const; + + /** Method to set the mask image */ + void SetMaskImage( const ImageType *image ); + + /** Method to get the mask image */ + const ImageType * GetMaskImage() const; + + /** method to get the Histogram */ + const HistogramType * GetOutput() const; + + /** method to get the Histogram */ + double* GetSiMatrix() const; + + /** + * Set the pixel value of the mask that should be considered "inside" the + * object. Defaults to 1. + */ + itkSetMacro( InsidePixelValue, PixelType ); + itkGetConstMacro( InsidePixelValue, PixelType ); + + protected: + EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter(); + virtual ~EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter() {}; + virtual void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; + + /** Standard itk::ProcessObject subclass method. */ + typedef DataObject::Pointer DataObjectPointer; + + typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType; + using Superclass::MakeOutput; + virtual DataObjectPointer MakeOutput( DataObjectPointerArraySizeType idx ) ITK_OVERRIDE; + + /** This method causes the filter to generate its output. */ + virtual void GenerateData() ITK_OVERRIDE; + + /** + * Normalize the direction of the offset before it is applied. + * The last non-zero dimension of the offest has to be positive in order + * to match to scanning order of the iterator. Only the sign is changed. + * For example, the input offset (-1, 0) will be normalized as + * (1, 0). + * */ + void NormalizeOffsetDirection(OffsetType &offset); + + private: + + unsigned int m_NumberOfBinsPerAxis; + PixelType m_Min; + PixelType m_Max; + RealType m_MinDistance; + RealType m_MaxDistance; + PixelType m_InsidePixelValue; + + MeasurementVectorType m_LowerBound; + MeasurementVectorType m_UpperBound; + OffsetVectorPointer m_Offsets; + + double * m_siMatrix; + }; + } // end of namespace Statistics +} // end of namespace itk + +#ifndef ITK_MANUAL_INSTANTIATION +#include "itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.hxx" +#endif + +#endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.hxx b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.hxx new file mode 100644 index 0000000000..8b3a423693 --- /dev/null +++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.hxx @@ -0,0 +1,400 @@ +/*=================================================================== + +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. + +===================================================================*/ + +/*========================================================================= +* +* Copyright Insight Software Consortium +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0.txt +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*=========================================================================*/ +#ifndef __itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter_hxx +#define __itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter_hxx + +#include "itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.h" + +#include "itkConstNeighborhoodIterator.h" +#include "itkNeighborhood.h" +#include "vnl/vnl_math.h" +#include "itkMacro.h" +#include "itkRescaleIntensityImageFilter.h" +#include "itkMaskImageFilter.h" +#include "itkLabelStatisticsImageFilter.h" +#include "itkScalarConnectedComponentImageFilter.h" +#include "itkRelabelComponentImageFilter.h" +#include "itkCastImageFilter.h" + +#include + +namespace itk +{ +namespace Statistics +{ +template +EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter +::EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter() : + m_NumberOfBinsPerAxis( itkGetStaticConstMacro( DefaultBinsPerAxis ) ), + m_Min( NumericTraits::NonpositiveMin() ), + m_Max( NumericTraits::max() ), + m_MinDistance( NumericTraits::ZeroValue() ), + m_MaxDistance( NumericTraits::max() ), + m_InsidePixelValue( NumericTraits::OneValue() ) +{ + this->SetNumberOfRequiredInputs( 1 ); + this->SetNumberOfRequiredOutputs( 1 ); + + const unsigned int measurementVectorSize = 1; + + this->ProcessObject::SetNthOutput( 0, this->MakeOutput( 0 ) ); + this->ProcessObject::SetNthOutput( 1, this->MakeOutput( 1 ) ); + HistogramType *output = const_cast( this->GetOutput() ); + output->SetMeasurementVectorSize( measurementVectorSize ); + + m_siMatrix = new double[m_NumberOfBinsPerAxis]; + for(unsigned int i = 0; i < m_NumberOfBinsPerAxis; i++) + { + m_siMatrix[i] = 0; + } + + this->m_LowerBound.SetSize( measurementVectorSize ); + this->m_UpperBound.SetSize( measurementVectorSize ); + + this->m_LowerBound[0] = this->m_Min; + this->m_LowerBound[1] = this->m_MinDistance; + this->m_UpperBound[0] = this->m_Max; + this->m_UpperBound[1] = this->m_MaxDistance; +} + +template +void +EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter +::SetOffset( const OffsetType offset ) +{ + OffsetVectorPointer offsetVector = OffsetVector::New(); + offsetVector->push_back( offset ); + this->SetOffsets( offsetVector ); + MITK_WARN << "We now have " << this->GetOffsets()->size() << " offsets in matrixgenerator"; +} + +template +void +EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter +::AddOffsets( const std::vector _offsets ) +{ + OffsetVectorPointer offsetVector = OffsetVector::New(); + typename OffsetVector::ConstIterator offsets; + //MITK_WARN << "We have " << this->GetOffsets()->size() << " offsets!"; + for( std::size_t i = 0; i < _offsets.size(); i++) + { + offsetVector->push_back(_offsets[i]); + auto k = _offsets[i]; + this->NormalizeOffsetDirection(k); + offsetVector->push_back(k); + } + this->SetOffsets( offsetVector ); + MITK_WARN << "We now have " << this->GetOffsets()->size() << " offsets in matrixgenerator"; +} + +template +void +EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter +::SetInput( const ImageType *image ) +{ + // Process object is not const-correct so the const_cast is required here + this->ProcessObject::SetNthInput( 0, const_cast( image ) ); +} + +template +void +EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter +::SetMaskImage( const ImageType *image ) +{ + // Process object is not const-correct so the const_cast is required here + this->ProcessObject::SetNthInput( 1, const_cast( image ) ); +} + +template +const TImageType * +EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter +::GetInput() const +{ + if( this->GetNumberOfInputs() < 1 ) + { + return ITK_NULLPTR; + } + return static_cast( this->ProcessObject::GetInput( 0 ) ); +} + +template +const TImageType * +EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter +::GetMaskImage() const +{ + if( this->GetNumberOfInputs() < 2 ) + { + return ITK_NULLPTR; + } + return static_cast( this->ProcessObject::GetInput( 1 ) ); +} + +template +const typename EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter::HistogramType * +EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter +::GetOutput() const +{ + const HistogramType *output = + static_cast( this->ProcessObject::GetOutput( 0 ) ); + return output; +} + + +template +double* EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter +::GetSiMatrix() const +{ + return m_siMatrix; +} + +template +typename EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter::DataObjectPointer +EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter +::MakeOutput( DataObjectPointerArraySizeType itkNotUsed( idx ) ) +{ + return HistogramType::New().GetPointer(); +} + +template +void +EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter +::GenerateData() +{ + HistogramType *output = + static_cast( this->ProcessObject::GetOutput( 0 ) ); + + const ImageType * inputImage = this->GetInput(); + const ImageType * maskImage = this->GetMaskImage(); + + // First, create an appropriate histogram with the right number of bins + // and mins and maxes correct for the image type. + typename HistogramType::SizeType size( output->GetMeasurementVectorSize() ); + + size.Fill( this->m_NumberOfBinsPerAxis ); + this->m_LowerBound[0] = this->m_Min; + this->m_LowerBound[1] = this->m_MinDistance; + this->m_UpperBound[0] = this->m_Max; + this->m_UpperBound[1] = this->m_MaxDistance; + + output->Initialize( size, this->m_LowerBound, this->m_UpperBound ); + + MeasurementVectorType run( output->GetMeasurementVectorSize() ); + typename HistogramType::IndexType hIndex; + + //Cast the image to a float image - with no respect to the incoming image + //to prevent some non-templated itk issues + typedef itk::Image FloatImageType; + typedef itk::CastImageFilter CastFilterType; + + typename CastFilterType::Pointer caster = CastFilterType::New(); + caster->SetInput(inputImage); + caster->Update(); + typename FloatImageType::Pointer floatImage = caster->GetOutput(); + + //Cast the mask to an unsigned short image - with no respect to the incomimg maskimage + //to prevent some non-templated itk issues + typedef unsigned short LabelPixelType; + typedef itk::Image LabelImageType; + + typedef itk::CastImageFilter MaskCastFilterType; + typename MaskCastFilterType::Pointer maskCaster = MaskCastFilterType::New(); + maskCaster->SetInput(maskImage); + maskCaster->Update(); + + //Set all values out of the mask to nans. + typedef itk::MaskImageFilter< FloatImageType, LabelImageType, FloatImageType > MaskFilterType; + typename MaskFilterType::Pointer maskFilter = MaskFilterType::New(); + maskFilter->SetInput(floatImage); + maskFilter->SetMaskImage(maskCaster->GetOutput()); + maskFilter->SetOutsideValue( std::numeric_limits::quiet_NaN()); + maskFilter->Update(); + FloatImageType::Pointer floatImageMasked = maskFilter->GetOutput(); + + typedef ConstNeighborhoodIterator NeighborhoodIteratorType; + typename NeighborhoodIteratorType::RadiusType radius; + radius.Fill( 1 ); + NeighborhoodIteratorType neighborIt( radius, + inputImage, inputImage->GetRequestedRegion() ); + + for( neighborIt.GoToBegin(); !neighborIt.IsAtEnd(); ++neighborIt ) + { + const PixelType centerPixelIntensity = neighborIt.GetCenterPixel(); + IndexType centerIndex = neighborIt.GetIndex(); + + FloatImageType::IndexType cIndex; + cIndex[0] = centerIndex[0]; + cIndex[1] = centerIndex[1]; + cIndex[2] = centerIndex[2]; + float centerFloatPixel = floatImageMasked->GetPixel(cIndex); + + int px = 0; + PixelType sum = 0.0; + bool canCalculate = true; + + typename OffsetVector::ConstIterator offsets; + for( offsets = this->GetOffsets()->Begin(); + offsets != this->GetOffsets()->End(); offsets++ ) + { + OffsetType offset = offsets.Value(); + IndexType index; + + index = centerIndex + offset; + + if(!inputImage->GetRequestedRegion().IsInside(index)) + { + canCalculate = false; + break; + } + + PixelType offsetPixel = inputImage->GetPixel(index); + + FloatImageType::IndexType fIndex; + fIndex[0] = index[0]; + fIndex[1] = index[1]; + fIndex[2] = index[2]; + + float floatPixel = floatImageMasked->GetPixel(fIndex); + + //We have a nan here + if(floatPixel != floatPixel || centerFloatPixel!= centerFloatPixel) + { + canCalculate = false; + break; + } + + sum += offsetPixel; + px++; + } + //If we have a nan in the neighbourhood, continue + if(!canCalculate) + continue; + + PixelType mean = sum / px; + + double si = std::abs(mean-centerPixelIntensity); + + run[0] = centerPixelIntensity; + + //Check for NaN and inf + if(run[0] == run[0] && !std::isinf(std::abs(run[0]))) + { + output->GetIndex( run, hIndex ); + output->IncreaseFrequencyOfIndex( hIndex, 1 ); + + m_siMatrix[hIndex[0]] += si; + } + //MITK_WARN << " -> In this round we added: " << centerIndex << " with value " << centerPixelIntensity << " and si = " << si; + //MITK_WARN << " -> Values are now siMatrix["< Values are now niMatrix: " << output->GetFrequency(hIndex) << "/" << run; + } +} + + +template +void +EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter +::SetPixelValueMinMax( PixelType min, PixelType max ) +{ + if( this->m_Min != min || this->m_Max != max ) + { + itkDebugMacro( "setting Min to " << min << "and Max to " << max ); + this->m_Min = min; + this->m_Max = max; + this->Modified(); + } +} + +template +void +EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter +::SetDistanceValueMinMax( RealType min, RealType max ) +{ + if( this->m_MinDistance != min || this->m_MaxDistance != max ) + { + itkDebugMacro( "setting MinDistance to " << min << "and MaxDistance to " + << max ); + this->m_MinDistance = min; + this->m_MaxDistance = max; + this->Modified(); + } +} + +template +void +EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter +::PrintSelf( std::ostream& os, Indent indent ) const +{ + Superclass::PrintSelf( os,indent ); + os << indent << "Offsets: " << this->GetOffsets() << std::endl; + os << indent << "Min: " << this->m_Min << std::endl; + os << indent << "Max: " << this->m_Max << std::endl; + os << indent << "Min distance: " << this->m_MinDistance << std::endl; + os << indent << "Max distance: " << this->m_MaxDistance << std::endl; + os << indent << "NumberOfBinsPerAxis: " << this->m_NumberOfBinsPerAxis + << std::endl; + os << indent << "InsidePixelValue: " << this->m_InsidePixelValue << std::endl; +} + +template +void +EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter +::NormalizeOffsetDirection(OffsetType &offset) +{ + //MITK_WARN <<" -> NGTDM old offset = " << offset; + itkDebugMacro("old offset = " << offset << std::endl); + int sign = 1; + bool metLastNonZero = false; + for (int i = offset.GetOffsetDimension()-1; i>=0; i--) + { + if (metLastNonZero) + { + offset[i] *= sign; + } + else if (offset[i] != 0) + { + sign = (offset[i] > 0 ) ? 1 : -1; + metLastNonZero = true; + offset[i] *= sign; + } + } + + //MITK_WARN << " ->NGTDM new offset = " << offset; + itkDebugMacro("new offset = " << offset << std::endl); +} +} // end of namespace Statistics +} // end of namespace itk + +#endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.h index 0c7392744b..013e000773 100644 --- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.h +++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.h @@ -1,245 +1,249 @@ /*=================================================================== 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. ===================================================================*/ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef __itkEnhancedScalarImageToRunLengthFeaturesFilter_h #define __itkEnhancedScalarImageToRunLengthFeaturesFilter_h #include "itkDataObjectDecorator.h" #include "itkEnhancedHistogramToRunLengthFeaturesFilter.h" #include "itkEnhancedScalarImageToRunLengthMatrixFilter.h" namespace itk { namespace Statistics { /** \class EnhancedScalarImageToRunLengthFeaturesFilter * \brief This class computes run length descriptions from an image. * * By default, run length features are computed for each spatial * direction and then averaged afterward, so it is possible to access the * standard deviations of the texture features. These values give a clue as * to texture anisotropy. However, doing this is much more work, because it * involved computing one for each offset given. To compute a single * matrix using the first offset, call FastCalculationsOn(). If this is called, * then the texture standard deviations will not be computed (and will be set * to zero), but texture computation will be much faster. * * This class is templated over the input image type. * * Template Parameters: * The image type, and the type of histogram frequency container. If you are * using a large number of bins per axis, a sparse frequency container may be * advisable. The default is to use a dense frequency container. * * Inputs and parameters: * -# An image * -# A mask defining the region over which texture features will be * calculated. (Optional) * -# The pixel value that defines the "inside" of the mask. (Optional, defaults * to 1 if a mask is set.) * -# The set of features to be calculated. These features are defined * in the HistogramToRunLengthFeaturesFilter class. * -# The number of intensity bins. (Optional, defaults to 256.) * -# The set of directions (offsets) to average across. (Optional, defaults to * {(-1, 0), (-1, -1), (0, -1), (1, -1)} for 2D images and scales analogously * for ND images.) * -# The pixel intensity range over which the features will be calculated. * (Optional, defaults to the full dynamic range of the pixel type.) * -# The distance range over which the features will be calculated. * (Optional, defaults to the full dynamic range of double type.) * * In general, the default parameter values should be sufficient. * * Outputs: * (1) The average value of each feature. * (2) The standard deviation in the values of each feature. * * Print references: * M. M. Galloway. Texture analysis using gray level run lengths. Computer * Graphics and Image Processing, 4:172-179, 1975. * * A. Chu, C. M. Sehgal, and J. F. Greenleaf. Use of gray value distribution of * run lengths for texture analysis. Pattern Recognition Letters, 11:415-420, * 1990. * * B. R. Dasarathy and E. B. Holder. Image characterizations based on joint * gray-level run-length distributions. Pattern Recognition Letters, 12:490-502, * 1991. * * IJ article: http://hdl.handle.net/1926/1374 * * \sa EnhancedScalarImageToRunLengthFeaturesFilter * \sa ScalarImageToRunLengthMatrixFilter * \sa HistogramToRunLengthFeaturesFilter * * \author: Nick Tustison * \ingroup ITKStatistics */ template< typename TImageType, typename THistogramFrequencyContainer = DenseFrequencyContainer2 > class EnhancedScalarImageToRunLengthFeaturesFilter:public ProcessObject { public: /** Standard typedefs */ typedef EnhancedScalarImageToRunLengthFeaturesFilter Self; typedef ProcessObject Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro(EnhancedScalarImageToRunLengthFeaturesFilter, ProcessObject); /** standard New() method support */ itkNewMacro(Self); typedef THistogramFrequencyContainer FrequencyContainerType; typedef TImageType ImageType; typedef typename ImageType::Pointer ImagePointer; typedef typename ImageType::PixelType PixelType; typedef typename ImageType::OffsetType OffsetType; typedef VectorContainer< unsigned char, OffsetType > OffsetVector; typedef typename OffsetVector::Pointer OffsetVectorPointer; typedef typename OffsetVector::ConstPointer OffsetVectorConstPointer; typedef EnhancedScalarImageToRunLengthMatrixFilter< ImageType, FrequencyContainerType > RunLengthMatrixFilterType; typedef typename RunLengthMatrixFilterType::HistogramType HistogramType; typedef EnhancedHistogramToRunLengthFeaturesFilter< HistogramType > RunLengthFeaturesFilterType; typedef short RunLengthFeatureName; typedef VectorContainer FeatureNameVector; typedef typename FeatureNameVector::Pointer FeatureNameVectorPointer; typedef typename FeatureNameVector::ConstPointer FeatureNameVectorConstPointer; typedef VectorContainer< unsigned char, double > FeatureValueVector; typedef typename FeatureValueVector::Pointer FeatureValueVectorPointer; /** Smart Pointer type to a DataObject. */ typedef DataObject::Pointer DataObjectPointer; /** Type of DataObjects used for scalar outputs */ typedef DataObjectDecorator< FeatureValueVector > FeatureValueVectorDataObjectType; const FeatureValueVectorDataObjectType * GetFeatureMeansOutput() const; const FeatureValueVectorDataObjectType * GetFeatureStandardDeviationsOutput() const; /** Connects the input image for which the features are going to be computed */ using Superclass::SetInput; void SetInput(const ImageType *); const ImageType * GetInput() const; /** Return the feature means and deviations. */ itkGetConstReferenceObjectMacro(FeatureMeans, FeatureValueVector); itkGetConstReferenceObjectMacro(FeatureStandardDeviations, FeatureValueVector); /** Set the desired feature set. Optional, for default value see above. */ itkSetConstObjectMacro(RequestedFeatures, FeatureNameVector); itkGetConstObjectMacro(RequestedFeatures, FeatureNameVector); /** Set the offsets over which the co-occurrence pairs will be computed. Optional; for default value see above. */ itkSetConstObjectMacro(Offsets, OffsetVector); itkGetConstObjectMacro(Offsets, OffsetVector); /** Set number of histogram bins along each axis. Optional; for default value see above. */ void SetNumberOfBinsPerAxis(unsigned int); /** Set the min and max (inclusive) pixel value that will be used for feature calculations. Optional; for default value see above. */ void SetPixelValueMinMax(PixelType min, PixelType max); /** Set the min and max (inclusive) pixel value that will be used for feature calculations. Optional; for default value see above. */ void SetDistanceValueMinMax( double min, double max ); /** Connects the mask image for which the histogram is going to be computed. Optional; for default value see above. */ void SetMaskImage(const ImageType *); const ImageType * GetMaskImage() const; /** Set the pixel value of the mask that should be considered "inside" the object. Optional; for default value see above. */ void SetInsidePixelValue(PixelType InsidePixelValue); itkGetConstMacro(FastCalculations, bool); itkSetMacro(FastCalculations, bool); itkBooleanMacro(FastCalculations); + itkGetConstMacro(CombinedFeatureCalculation, bool); + itkSetMacro(CombinedFeatureCalculation, bool); + itkBooleanMacro(CombinedFeatureCalculation); protected: EnhancedScalarImageToRunLengthFeaturesFilter(); ~EnhancedScalarImageToRunLengthFeaturesFilter() override {} void PrintSelf( std::ostream & os, Indent indent ) const ITK_OVERRIDE; void FastCompute(); void FullCompute(); /** This method causes the filter to generate its output. */ void GenerateData() ITK_OVERRIDE; /** Make a DataObject to be used for output output. */ typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType; using Superclass::MakeOutput; DataObjectPointer MakeOutput(DataObjectPointerArraySizeType) ITK_OVERRIDE; private: typename RunLengthMatrixFilterType::Pointer m_RunLengthMatrixGenerator; FeatureValueVectorPointer m_FeatureMeans; FeatureValueVectorPointer m_FeatureStandardDeviations; FeatureNameVectorConstPointer m_RequestedFeatures; OffsetVectorConstPointer m_Offsets; bool m_FastCalculations; + bool m_CombinedFeatureCalculation; }; } // end of namespace Statistics } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkEnhancedScalarImageToRunLengthFeaturesFilter.hxx" #endif #endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.hxx b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.hxx index bda0fccfc2..c4d2029663 100644 --- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.hxx +++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.hxx @@ -1,410 +1,435 @@ /*=================================================================== 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. ===================================================================*/ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef __itkEnhancedScalarImageToRunLengthFeaturesFilter_hxx #define __itkEnhancedScalarImageToRunLengthFeaturesFilter_hxx #include "itkEnhancedScalarImageToRunLengthFeaturesFilter.h" #include "itkNeighborhood.h" #include #include "vnl/vnl_math.h" namespace itk { namespace Statistics { template EnhancedScalarImageToRunLengthFeaturesFilter - ::EnhancedScalarImageToRunLengthFeaturesFilter() + ::EnhancedScalarImageToRunLengthFeaturesFilter() : + m_CombinedFeatureCalculation(false) { this->SetNumberOfRequiredInputs( 1 ); this->SetNumberOfRequiredOutputs( 1 ); for( int i = 0; i < 2; ++i ) { this->ProcessObject::SetNthOutput( i, this->MakeOutput( i ) ); } this->m_RunLengthMatrixGenerator = RunLengthMatrixFilterType::New(); this->m_FeatureMeans = FeatureValueVector::New(); this->m_FeatureStandardDeviations = FeatureValueVector::New(); // Set the requested features to the default value: // {Energy, Entropy, InverseDifferenceMoment, Inertia, ClusterShade, // ClusterProminence} FeatureNameVectorPointer requestedFeatures = FeatureNameVector::New(); // can't directly set this->m_RequestedFeatures since it is const! requestedFeatures->push_back( RunLengthFeaturesFilterType::ShortRunEmphasis ); requestedFeatures->push_back( RunLengthFeaturesFilterType::LongRunEmphasis ); requestedFeatures->push_back( RunLengthFeaturesFilterType::GreyLevelNonuniformity ); + requestedFeatures->push_back( RunLengthFeaturesFilterType::GreyLevelNonuniformityNormalized ); requestedFeatures->push_back( RunLengthFeaturesFilterType::RunLengthNonuniformity ); + requestedFeatures->push_back( RunLengthFeaturesFilterType::RunLengthNonuniformityNormalized ); requestedFeatures->push_back( RunLengthFeaturesFilterType::LowGreyLevelRunEmphasis ); requestedFeatures->push_back( RunLengthFeaturesFilterType::HighGreyLevelRunEmphasis ); requestedFeatures->push_back( RunLengthFeaturesFilterType::ShortRunLowGreyLevelEmphasis ); requestedFeatures->push_back( RunLengthFeaturesFilterType::ShortRunHighGreyLevelEmphasis ); requestedFeatures->push_back( RunLengthFeaturesFilterType::LongRunLowGreyLevelEmphasis ); requestedFeatures->push_back( RunLengthFeaturesFilterType::LongRunHighGreyLevelEmphasis ); requestedFeatures->push_back( 20 ); this->SetRequestedFeatures( requestedFeatures ); // Set the offset directions to their defaults: half of all the possible // directions 1 pixel away. (The other half is included by symmetry.) // We use a neighborhood iterator to calculate the appropriate offsets. typedef Neighborhood NeighborhoodType; NeighborhoodType hood; hood.SetRadius( 1 ); // select all "previous" neighbors that are face+edge+vertex // connected to the current pixel. do not include the center pixel. unsigned int centerIndex = hood.GetCenterNeighborhoodIndex(); OffsetVectorPointer offsets = OffsetVector::New(); for( unsigned int d = 0; d < centerIndex; d++ ) { OffsetType offset = hood.GetOffset( d ); offsets->push_back( offset ); } this->SetOffsets( offsets ); this->m_FastCalculations = false; } template typename EnhancedScalarImageToRunLengthFeaturesFilter ::DataObjectPointer EnhancedScalarImageToRunLengthFeaturesFilter ::MakeOutput( DataObjectPointerArraySizeType itkNotUsed(idx) ) { return FeatureValueVectorDataObjectType::New().GetPointer(); } template void EnhancedScalarImageToRunLengthFeaturesFilter ::GenerateData(void) { if ( this->m_FastCalculations ) { this->FastCompute(); } else { this->FullCompute(); } } template void EnhancedScalarImageToRunLengthFeaturesFilter ::FullCompute() { int numOffsets = this->m_Offsets->size(); + if (m_CombinedFeatureCalculation) + { + numOffsets = 1; + } int numFeatures = this->m_RequestedFeatures->size(); double **features; features = new double *[numOffsets]; for( int i = 0; i < numOffsets; i++ ) { features[i] = new double[numFeatures]; } unsigned long numberOfVoxels = 0; ImageRegionConstIterator voxelCountIter(this->GetMaskImage(),this->GetMaskImage()->GetLargestPossibleRegion()); while ( ! voxelCountIter.IsAtEnd() ) { if (voxelCountIter.Get() > 0) ++numberOfVoxels; ++voxelCountIter; } // For each offset, calculate each feature typename OffsetVector::ConstIterator offsetIt; int offsetNum, featureNum; typedef typename RunLengthFeaturesFilterType::RunLengthFeatureName InternalRunLengthFeatureName; + OffsetVectorPointer offsets = OffsetVector::New(); + if (m_CombinedFeatureCalculation) + { + for (int i = 0; i < this->m_Offsets->Size(); ++i) + { + offsets->push_back(m_Offsets->ElementAt(i)); + } + } + + for( offsetIt = this->m_Offsets->Begin(), offsetNum = 0; offsetIt != this->m_Offsets->End(); offsetIt++, offsetNum++ ) { - this->m_RunLengthMatrixGenerator->SetOffset( offsetIt.Value() ); + this->m_RunLengthMatrixGenerator->SetOffset(offsetIt.Value()); + if (m_CombinedFeatureCalculation) + { + this->m_RunLengthMatrixGenerator->SetOffsets(offsets); + } this->m_RunLengthMatrixGenerator->Update(); typename RunLengthFeaturesFilterType::Pointer runLengthMatrixCalculator = RunLengthFeaturesFilterType::New(); runLengthMatrixCalculator->SetInput( this->m_RunLengthMatrixGenerator->GetOutput() ); runLengthMatrixCalculator->SetNumberOfVoxels(numberOfVoxels); runLengthMatrixCalculator->Update(); typename FeatureNameVector::ConstIterator fnameIt; for( fnameIt = this->m_RequestedFeatures->Begin(), featureNum = 0; fnameIt != this->m_RequestedFeatures->End(); fnameIt++, featureNum++ ) { features[offsetNum][featureNum] = runLengthMatrixCalculator->GetFeature( ( InternalRunLengthFeatureName )fnameIt.Value() ); } + if (m_CombinedFeatureCalculation) + { + break; + } } // Now get the mean and deviaton of each feature across the offsets. this->m_FeatureMeans->clear(); this->m_FeatureStandardDeviations->clear(); double *tempFeatureMeans = new double[numFeatures]; double *tempFeatureDevs = new double[numFeatures]; /*Compute incremental mean and SD, a la Knuth, "The Art of Computer Programming, Volume 2: Seminumerical Algorithms", section 4.2.2. Compute mean and standard deviation using the recurrence relation: M(1) = x(1), M(k) = M(k-1) + (x(k) - M(k-1) ) / k S(1) = 0, S(k) = S(k-1) + (x(k) - M(k-1)) * (x(k) - M(k)) for 2 <= k <= n, then sigma = std::sqrt(S(n) / n) (or divide by n-1 for sample SD instead of population SD). */ // Set up the initial conditions (k = 1) for( featureNum = 0; featureNum < numFeatures; featureNum++ ) { tempFeatureMeans[featureNum] = features[0][featureNum]; tempFeatureDevs[featureNum] = 0; } // Run through the recurrence (k = 2 ... N) for( offsetNum = 1; offsetNum < numOffsets; offsetNum++ ) { int k = offsetNum + 1; for( featureNum = 0; featureNum < numFeatures; featureNum++ ) { double M_k_minus_1 = tempFeatureMeans[featureNum]; double S_k_minus_1 = tempFeatureDevs[featureNum]; double x_k = features[offsetNum][featureNum]; double M_k = M_k_minus_1 + ( x_k - M_k_minus_1 ) / k; double S_k = S_k_minus_1 + ( x_k - M_k_minus_1 ) * ( x_k - M_k ); tempFeatureMeans[featureNum] = M_k; tempFeatureDevs[featureNum] = S_k; } } for( featureNum = 0; featureNum < numFeatures; featureNum++ ) { tempFeatureDevs[featureNum] = std::sqrt( tempFeatureDevs[featureNum] / numOffsets ); this->m_FeatureMeans->push_back( tempFeatureMeans[featureNum] ); this->m_FeatureStandardDeviations->push_back( tempFeatureDevs[featureNum] ); } FeatureValueVectorDataObjectType *meanOutputObject = itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 0 ) ); meanOutputObject->Set( this->m_FeatureMeans ); FeatureValueVectorDataObjectType *standardDeviationOutputObject = itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 1 ) ); standardDeviationOutputObject->Set( this->m_FeatureStandardDeviations ); delete[] tempFeatureMeans; delete[] tempFeatureDevs; for( int i = 0; i < numOffsets; i++ ) { delete[] features[i]; } delete[] features; } template void EnhancedScalarImageToRunLengthFeaturesFilter ::FastCompute() { // Compute the feature for the first offset typename OffsetVector::ConstIterator offsetIt = this->m_Offsets->Begin(); this->m_RunLengthMatrixGenerator->SetOffset( offsetIt.Value() ); this->m_RunLengthMatrixGenerator->Update(); typename RunLengthFeaturesFilterType::Pointer runLengthMatrixCalculator = RunLengthFeaturesFilterType::New(); runLengthMatrixCalculator->SetInput( this->m_RunLengthMatrixGenerator->GetOutput() ); runLengthMatrixCalculator->Update(); typedef typename RunLengthFeaturesFilterType::RunLengthFeatureName InternalRunLengthFeatureName; this->m_FeatureMeans->clear(); this->m_FeatureStandardDeviations->clear(); typename FeatureNameVector::ConstIterator fnameIt; for( fnameIt = this->m_RequestedFeatures->Begin(); fnameIt != this->m_RequestedFeatures->End(); fnameIt++ ) { this->m_FeatureMeans->push_back( runLengthMatrixCalculator->GetFeature( ( InternalRunLengthFeatureName )fnameIt.Value() ) ); this->m_FeatureStandardDeviations->push_back( 0.0 ); } FeatureValueVectorDataObjectType *meanOutputObject = itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 0 ) ); meanOutputObject->Set( this->m_FeatureMeans ); FeatureValueVectorDataObjectType *standardDeviationOutputObject = itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 1 ) ); standardDeviationOutputObject->Set( this->m_FeatureStandardDeviations ); } template void EnhancedScalarImageToRunLengthFeaturesFilter ::SetInput( const ImageType *image ) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput( 0, const_cast( image ) ); this->m_RunLengthMatrixGenerator->SetInput( image ); } template void EnhancedScalarImageToRunLengthFeaturesFilter ::SetNumberOfBinsPerAxis( unsigned int numberOfBins ) { itkDebugMacro( "setting NumberOfBinsPerAxis to " << numberOfBins ); this->m_RunLengthMatrixGenerator->SetNumberOfBinsPerAxis( numberOfBins ); this->Modified(); } template void EnhancedScalarImageToRunLengthFeaturesFilter ::SetPixelValueMinMax( PixelType min, PixelType max ) { itkDebugMacro( "setting Min to " << min << "and Max to " << max ); this->m_RunLengthMatrixGenerator->SetPixelValueMinMax( min, max ); this->Modified(); } template void EnhancedScalarImageToRunLengthFeaturesFilter ::SetDistanceValueMinMax( double min, double max ) { itkDebugMacro( "setting Min to " << min << "and Max to " << max ); this->m_RunLengthMatrixGenerator->SetDistanceValueMinMax( min, max ); this->Modified(); } template void EnhancedScalarImageToRunLengthFeaturesFilter ::SetMaskImage( const ImageType *image ) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput( 1, const_cast< ImageType * >( image ) ); this->m_RunLengthMatrixGenerator->SetMaskImage( image ); } template const TImage * EnhancedScalarImageToRunLengthFeaturesFilter ::GetInput() const { if ( this->GetNumberOfInputs() < 1 ) { return ITK_NULLPTR; } return static_cast( this->ProcessObject::GetInput( 0 ) ); } template const typename EnhancedScalarImageToRunLengthFeaturesFilter ::FeatureValueVectorDataObjectType * EnhancedScalarImageToRunLengthFeaturesFilter ::GetFeatureMeansOutput() const { return itkDynamicCastInDebugMode (this->ProcessObject::GetOutput( 0 ) ); } template const typename EnhancedScalarImageToRunLengthFeaturesFilter ::FeatureValueVectorDataObjectType * EnhancedScalarImageToRunLengthFeaturesFilter ::GetFeatureStandardDeviationsOutput() const { return itkDynamicCastInDebugMode< const FeatureValueVectorDataObjectType * > ( this->ProcessObject::GetOutput( 1 ) ); } template const TImage * EnhancedScalarImageToRunLengthFeaturesFilter ::GetMaskImage() const { if ( this->GetNumberOfInputs() < 2 ) { return ITK_NULLPTR; } return static_cast< const ImageType *>( this->ProcessObject::GetInput( 1 ) ); } template void EnhancedScalarImageToRunLengthFeaturesFilter ::SetInsidePixelValue( PixelType insidePixelValue ) { itkDebugMacro( "setting InsidePixelValue to " << insidePixelValue ); this->m_RunLengthMatrixGenerator->SetInsidePixelValue( insidePixelValue ); this->Modified(); } template void EnhancedScalarImageToRunLengthFeaturesFilter ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "RequestedFeatures: " << this->GetRequestedFeatures() << std::endl; os << indent << "FeatureStandardDeviations: " << this->GetFeatureStandardDeviations() << std::endl; os << indent << "FastCalculations: " << this->GetFastCalculations() << std::endl; os << indent << "Offsets: " << this->GetOffsets() << std::endl; os << indent << "FeatureMeans: " << this->GetFeatureMeans() << std::endl; } } // end of namespace Statistics } // end of namespace itk -#endif \ No newline at end of file +#endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.hxx b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.hxx index 4c4ac9770f..fc3d130ce4 100644 --- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.hxx +++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.hxx @@ -1,404 +1,429 @@ /*=================================================================== 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. ===================================================================*/ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef __itkEnhancedScalarImageToRunLengthMatrixFilter_hxx #define __itkEnhancedScalarImageToRunLengthMatrixFilter_hxx #include "itkEnhancedScalarImageToRunLengthMatrixFilter.h" #include "itkConstNeighborhoodIterator.h" #include "itkNeighborhood.h" #include "vnl/vnl_math.h" #include "itkMacro.h" namespace itk { namespace Statistics { template EnhancedScalarImageToRunLengthMatrixFilter ::EnhancedScalarImageToRunLengthMatrixFilter() : m_NumberOfBinsPerAxis( itkGetStaticConstMacro( DefaultBinsPerAxis ) ), m_Min( NumericTraits::NonpositiveMin() ), m_Max( NumericTraits::max() ), m_MinDistance( NumericTraits::ZeroValue() ), m_MaxDistance( NumericTraits::max() ), m_InsidePixelValue( NumericTraits::OneValue() ) { this->SetNumberOfRequiredInputs( 1 ); this->SetNumberOfRequiredOutputs( 1 ); const unsigned int measurementVectorSize = 2; this->ProcessObject::SetNthOutput( 0, this->MakeOutput( 0 ) ); HistogramType *output = const_cast( this->GetOutput() ); output->SetMeasurementVectorSize( measurementVectorSize ); this->m_LowerBound.SetSize( measurementVectorSize ); this->m_UpperBound.SetSize( measurementVectorSize ); this->m_LowerBound[0] = this->m_Min; this->m_LowerBound[1] = this->m_MinDistance; this->m_UpperBound[0] = this->m_Max; this->m_UpperBound[1] = this->m_MaxDistance; } template void EnhancedScalarImageToRunLengthMatrixFilter ::SetOffset( const OffsetType offset ) { OffsetVectorPointer offsetVector = OffsetVector::New(); offsetVector->push_back( offset ); this->SetOffsets( offsetVector ); } template void EnhancedScalarImageToRunLengthMatrixFilter ::SetInput( const ImageType *image ) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput( 0, const_cast( image ) ); } template void EnhancedScalarImageToRunLengthMatrixFilter ::SetMaskImage( const ImageType *image ) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput( 1, const_cast( image ) ); } template const TImageType * EnhancedScalarImageToRunLengthMatrixFilter ::GetInput() const { if( this->GetNumberOfInputs() < 1 ) { return ITK_NULLPTR; } return static_cast( this->ProcessObject::GetInput( 0 ) ); } template const TImageType * EnhancedScalarImageToRunLengthMatrixFilter ::GetMaskImage() const { if( this->GetNumberOfInputs() < 2 ) { return ITK_NULLPTR; } return static_cast( this->ProcessObject::GetInput( 1 ) ); } template const typename EnhancedScalarImageToRunLengthMatrixFilter::HistogramType * EnhancedScalarImageToRunLengthMatrixFilter ::GetOutput() const { const HistogramType *output = static_cast( this->ProcessObject::GetOutput( 0 ) ); return output; } template typename EnhancedScalarImageToRunLengthMatrixFilter::DataObjectPointer EnhancedScalarImageToRunLengthMatrixFilter ::MakeOutput( DataObjectPointerArraySizeType itkNotUsed( idx ) ) { return HistogramType::New().GetPointer(); } template void EnhancedScalarImageToRunLengthMatrixFilter ::GenerateData() { HistogramType *output = static_cast( this->ProcessObject::GetOutput( 0 ) ); const ImageType * inputImage = this->GetInput(); // First, create an appropriate histogram with the right number of bins // and mins and maxes correct for the image type. typename HistogramType::SizeType size( output->GetMeasurementVectorSize() ); size.Fill( this->m_NumberOfBinsPerAxis ); this->m_LowerBound[0] = this->m_Min; this->m_LowerBound[1] = this->m_MinDistance; this->m_UpperBound[0] = this->m_Max; this->m_UpperBound[1] = this->m_MaxDistance; output->Initialize( size, this->m_LowerBound, this->m_UpperBound ); MeasurementVectorType run( output->GetMeasurementVectorSize() ); typename HistogramType::IndexType hIndex; // Iterate over all of those pixels and offsets, adding each // distance/intensity pair to the histogram typedef ConstNeighborhoodIterator NeighborhoodIteratorType; typename NeighborhoodIteratorType::RadiusType radius; radius.Fill( 1 ); NeighborhoodIteratorType neighborIt( radius, inputImage, inputImage->GetRequestedRegion() ); // this temp image has the same dimension for each offset // moving the allocation out of loop of offsets // while keeping FillBuffer with boolean false in each loop typedef Image BoolImageType; typename BoolImageType::Pointer alreadyVisitedImage = BoolImageType::New(); alreadyVisitedImage->CopyInformation( inputImage ); alreadyVisitedImage->SetRegions( inputImage->GetRequestedRegion() ); alreadyVisitedImage->Allocate(); typename OffsetVector::ConstIterator offsets; for( offsets = this->GetOffsets()->Begin(); offsets != this->GetOffsets()->End(); offsets++ ) { alreadyVisitedImage->FillBuffer( false ); neighborIt.GoToBegin(); OffsetType offset = offsets.Value(); this->NormalizeOffsetDirection(offset); for( neighborIt.GoToBegin(); !neighborIt.IsAtEnd(); ++neighborIt ) { const PixelType centerPixelIntensity = neighborIt.GetCenterPixel(); + if (centerPixelIntensity != centerPixelIntensity) // Check for invalid values + { + continue; + } IndexType centerIndex = neighborIt.GetIndex(); if( centerPixelIntensity < this->m_Min || centerPixelIntensity > this->m_Max || alreadyVisitedImage->GetPixel( centerIndex ) || ( this->GetMaskImage() && this->GetMaskImage()->GetPixel( centerIndex ) != this->m_InsidePixelValue ) ) { continue; // don't put a pixel in the histogram if the value // is out-of-bounds or is outside the mask. } itkDebugMacro("===> offset = " << offset << std::endl); MeasurementType centerBinMin = this->GetOutput()-> GetBinMinFromValue( 0, centerPixelIntensity ); MeasurementType centerBinMax = this->GetOutput()-> GetBinMaxFromValue( 0, centerPixelIntensity ); MeasurementType lastBinMax = this->GetOutput()-> GetDimensionMaxs( 0 )[ this->GetOutput()->GetSize( 0 ) - 1 ]; PixelType pixelIntensity( NumericTraits::ZeroValue() ); IndexType index; + int steps = 0; index = centerIndex + offset; IndexType lastGoodIndex = centerIndex; bool runLengthSegmentAlreadyVisited = false; // Scan from the current pixel at index, following // the direction of offset. Run length is computed as the // length of continuous pixels whose pixel values are // in the same bin. while ( inputImage->GetRequestedRegion().IsInside(index) ) { + pixelIntensity = inputImage->GetPixel(index); // For the same offset, each run length segment can // only be visited once if (alreadyVisitedImage->GetPixel( index ) ) { runLengthSegmentAlreadyVisited = true; break; } + if (pixelIntensity != pixelIntensity) + { + break; + } - pixelIntensity = inputImage->GetPixel( index ); // Special attention paid to boundaries of bins. // For the last bin, // it is left close and right close (following the previous // gerrit patch). // For all // other bins, // the bin is left close and right open. if ( pixelIntensity >= centerBinMin - && ( pixelIntensity < centerBinMax || ( pixelIntensity == centerBinMax && centerBinMax == lastBinMax ) ) ) + && ( pixelIntensity < centerBinMax || ( pixelIntensity == centerBinMax && centerBinMax == lastBinMax ) ) + && (!this->GetMaskImage() || this->GetMaskImage()->GetPixel(index) == this->m_InsidePixelValue)) { alreadyVisitedImage->SetPixel( index, true ); lastGoodIndex = index; index += offset; + steps++; } else { break; } } if ( runLengthSegmentAlreadyVisited ) { + MITK_INFO << "Already visited 1 " << index; continue; } IndexType lastGoodIndex2 = lastGoodIndex; index = centerIndex - offset; lastGoodIndex = centerIndex; while ( inputImage->GetRequestedRegion().IsInside(index) ) { + pixelIntensity = inputImage->GetPixel(index); + if (pixelIntensity != pixelIntensity) + { + break; + } if (alreadyVisitedImage->GetPixel( index ) ) { - runLengthSegmentAlreadyVisited = true; + if (pixelIntensity >= centerBinMin + && (pixelIntensity < centerBinMax || (pixelIntensity == centerBinMax && centerBinMax == lastBinMax))) + { + runLengthSegmentAlreadyVisited = true; + } break; } - pixelIntensity = inputImage->GetPixel( index ); if ( pixelIntensity >= centerBinMin - && ( pixelIntensity < centerBinMax || ( pixelIntensity == centerBinMax && centerBinMax == lastBinMax ) ) ) + && ( pixelIntensity < centerBinMax || ( pixelIntensity == centerBinMax && centerBinMax == lastBinMax ) ) + && (!this->GetMaskImage() || this->GetMaskImage()->GetPixel(index) == this->m_InsidePixelValue)) { alreadyVisitedImage->SetPixel( index, true ); lastGoodIndex = index; + steps++; index -= offset; } else break; } - if ( runLengthSegmentAlreadyVisited ) + if (runLengthSegmentAlreadyVisited) + { + MITK_INFO << "Already visited 2 " << index; continue; - + } PointType centerPoint; inputImage->TransformIndexToPhysicalPoint( centerIndex, centerPoint ); PointType point; inputImage->TransformIndexToPhysicalPoint( lastGoodIndex, point ); PointType point2; inputImage->TransformIndexToPhysicalPoint( lastGoodIndex2, point2 ); run[0] = centerPixelIntensity; - run[1] = point.EuclideanDistanceTo( point2 ); + run[1] = steps; + //run[1] = point.EuclideanDistanceTo( point2 ); if( run[1] >= this->m_MinDistance && run[1] <= this->m_MaxDistance ) { output->GetIndex( run, hIndex ); output->IncreaseFrequencyOfIndex( hIndex, 1 ); itkDebugStatement(typename HistogramType::IndexType tempMeasurementIndex;) itkDebugStatement(output->GetIndex(run,tempMeasurementIndex);) itkDebugMacro( "centerIndex<->index: " << static_cast( centerPixelIntensity ) << "@"<< centerIndex << "<->" << static_cast( pixelIntensity ) << "@" << index <<", Bin# " << tempMeasurementIndex << ", Measurement: (" << run[0] << ", " << run[1] << ")" << ", Center bin [" << this->GetOutput()->GetBinMinFromValue( 0, run[0] ) << "," << this->GetOutput()->GetBinMaxFromValue( 0, run[0] ) << "]" << "~[" << this->GetOutput()->GetBinMinFromValue( 1, run[1] ) << "," << this->GetOutput()->GetBinMaxFromValue( 1, run[1] ) << "]" << std::endl ); } } } } template void EnhancedScalarImageToRunLengthMatrixFilter ::SetPixelValueMinMax( PixelType min, PixelType max ) { if( this->m_Min != min || this->m_Max != max ) { itkDebugMacro( "setting Min to " << min << "and Max to " << max ); this->m_Min = min; this->m_Max = max; this->Modified(); } } template void EnhancedScalarImageToRunLengthMatrixFilter ::SetDistanceValueMinMax( RealType min, RealType max ) { if( this->m_MinDistance != min || this->m_MaxDistance != max ) { itkDebugMacro( "setting MinDistance to " << min << "and MaxDistance to " << max ); this->m_MinDistance = min; this->m_MaxDistance = max; this->Modified(); } } template void EnhancedScalarImageToRunLengthMatrixFilter ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf( os,indent ); os << indent << "Offsets: " << this->GetOffsets() << std::endl; os << indent << "Min: " << this->m_Min << std::endl; os << indent << "Max: " << this->m_Max << std::endl; os << indent << "Min distance: " << this->m_MinDistance << std::endl; os << indent << "Max distance: " << this->m_MaxDistance << std::endl; os << indent << "NumberOfBinsPerAxis: " << this->m_NumberOfBinsPerAxis << std::endl; os << indent << "InsidePixelValue: " << this->m_InsidePixelValue << std::endl; } template void EnhancedScalarImageToRunLengthMatrixFilter ::NormalizeOffsetDirection(OffsetType &offset) { itkDebugMacro("old offset = " << offset << std::endl); int sign = 1; bool metLastNonZero = false; for (int i = offset.GetOffsetDimension()-1; i>=0; i--) { if (metLastNonZero) { offset[i] *= sign; } else if (offset[i] != 0) { sign = (offset[i] > 0 ) ? 1 : -1; metLastNonZero = true; offset[i] *= sign; } } itkDebugMacro("new offset = " << offset << std::endl); } } // end of namespace Statistics } // end of namespace itk #endif \ No newline at end of file diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneFeaturesFilter.h similarity index 84% copy from Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.h copy to Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneFeaturesFilter.h index 0c7392744b..cdb4bd4235 100644 --- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.h +++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneFeaturesFilter.h @@ -1,245 +1,245 @@ /*=================================================================== 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. ===================================================================*/ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ -#ifndef __itkEnhancedScalarImageToRunLengthFeaturesFilter_h -#define __itkEnhancedScalarImageToRunLengthFeaturesFilter_h +#ifndef __itkEnhancedScalarImageToSizeZoneFeaturesFilter_h +#define __itkEnhancedScalarImageToSizeZoneFeaturesFilter_h #include "itkDataObjectDecorator.h" -#include "itkEnhancedHistogramToRunLengthFeaturesFilter.h" -#include "itkEnhancedScalarImageToRunLengthMatrixFilter.h" +#include "itkEnhancedHistogramToSizeZoneFeaturesFilter.h" +#include "itkEnhancedScalarImageToSizeZoneMatrixFilter.h" namespace itk { namespace Statistics { - /** \class EnhancedScalarImageToRunLengthFeaturesFilter + /** \class EnhancedScalarImageToSizeZoneFeaturesFilter * \brief This class computes run length descriptions from an image. * * By default, run length features are computed for each spatial * direction and then averaged afterward, so it is possible to access the * standard deviations of the texture features. These values give a clue as * to texture anisotropy. However, doing this is much more work, because it * involved computing one for each offset given. To compute a single * matrix using the first offset, call FastCalculationsOn(). If this is called, * then the texture standard deviations will not be computed (and will be set * to zero), but texture computation will be much faster. * * This class is templated over the input image type. * * Template Parameters: * The image type, and the type of histogram frequency container. If you are * using a large number of bins per axis, a sparse frequency container may be * advisable. The default is to use a dense frequency container. * * Inputs and parameters: * -# An image * -# A mask defining the region over which texture features will be * calculated. (Optional) * -# The pixel value that defines the "inside" of the mask. (Optional, defaults * to 1 if a mask is set.) * -# The set of features to be calculated. These features are defined - * in the HistogramToRunLengthFeaturesFilter class. + * in the HistogramToSizeZoneFeaturesFilter class. * -# The number of intensity bins. (Optional, defaults to 256.) * -# The set of directions (offsets) to average across. (Optional, defaults to * {(-1, 0), (-1, -1), (0, -1), (1, -1)} for 2D images and scales analogously * for ND images.) * -# The pixel intensity range over which the features will be calculated. * (Optional, defaults to the full dynamic range of the pixel type.) * -# The distance range over which the features will be calculated. * (Optional, defaults to the full dynamic range of double type.) * * In general, the default parameter values should be sufficient. * * Outputs: * (1) The average value of each feature. * (2) The standard deviation in the values of each feature. * * Print references: * M. M. Galloway. Texture analysis using gray level run lengths. Computer * Graphics and Image Processing, 4:172-179, 1975. * * A. Chu, C. M. Sehgal, and J. F. Greenleaf. Use of gray value distribution of * run lengths for texture analysis. Pattern Recognition Letters, 11:415-420, * 1990. * * B. R. Dasarathy and E. B. Holder. Image characterizations based on joint * gray-level run-length distributions. Pattern Recognition Letters, 12:490-502, * 1991. * * IJ article: http://hdl.handle.net/1926/1374 * - * \sa EnhancedScalarImageToRunLengthFeaturesFilter - * \sa ScalarImageToRunLengthMatrixFilter - * \sa HistogramToRunLengthFeaturesFilter + * \sa EnhancedScalarImageToSizeZoneFeaturesFilter + * \sa ScalarImageToSizeZoneMatrixFilter + * \sa HistogramToSizeZoneFeaturesFilter * * \author: Nick Tustison * \ingroup ITKStatistics */ template< typename TImageType, typename THistogramFrequencyContainer = DenseFrequencyContainer2 > - class EnhancedScalarImageToRunLengthFeaturesFilter:public ProcessObject + class EnhancedScalarImageToSizeZoneFeaturesFilter:public ProcessObject { public: /** Standard typedefs */ - typedef EnhancedScalarImageToRunLengthFeaturesFilter Self; + typedef EnhancedScalarImageToSizeZoneFeaturesFilter Self; typedef ProcessObject Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; /** Run-time type information (and related methods). */ - itkTypeMacro(EnhancedScalarImageToRunLengthFeaturesFilter, ProcessObject); + itkTypeMacro(EnhancedScalarImageToSizeZoneFeaturesFilter, ProcessObject); /** standard New() method support */ itkNewMacro(Self); typedef THistogramFrequencyContainer FrequencyContainerType; typedef TImageType ImageType; typedef typename ImageType::Pointer ImagePointer; typedef typename ImageType::PixelType PixelType; typedef typename ImageType::OffsetType OffsetType; typedef VectorContainer< unsigned char, OffsetType > OffsetVector; typedef typename OffsetVector::Pointer OffsetVectorPointer; typedef typename OffsetVector::ConstPointer OffsetVectorConstPointer; - typedef EnhancedScalarImageToRunLengthMatrixFilter< - ImageType, FrequencyContainerType > RunLengthMatrixFilterType; + typedef EnhancedScalarImageToSizeZoneMatrixFilter< + ImageType, FrequencyContainerType > SizeZoneMatrixFilterType; - typedef typename RunLengthMatrixFilterType::HistogramType + typedef typename SizeZoneMatrixFilterType::HistogramType HistogramType; - typedef EnhancedHistogramToRunLengthFeaturesFilter< HistogramType > - RunLengthFeaturesFilterType; + typedef EnhancedHistogramToSizeZoneFeaturesFilter< HistogramType > + SizeZoneFeaturesFilterType; - typedef short RunLengthFeatureName; + typedef short SizeZoneFeatureName; typedef VectorContainer FeatureNameVector; + SizeZoneFeatureName> FeatureNameVector; typedef typename FeatureNameVector::Pointer FeatureNameVectorPointer; typedef typename FeatureNameVector::ConstPointer FeatureNameVectorConstPointer; typedef VectorContainer< unsigned char, double > FeatureValueVector; typedef typename FeatureValueVector::Pointer FeatureValueVectorPointer; /** Smart Pointer type to a DataObject. */ typedef DataObject::Pointer DataObjectPointer; /** Type of DataObjects used for scalar outputs */ typedef DataObjectDecorator< FeatureValueVector > FeatureValueVectorDataObjectType; const FeatureValueVectorDataObjectType * GetFeatureMeansOutput() const; const FeatureValueVectorDataObjectType * GetFeatureStandardDeviationsOutput() const; /** Connects the input image for which the features are going to be computed */ using Superclass::SetInput; void SetInput(const ImageType *); const ImageType * GetInput() const; /** Return the feature means and deviations. */ itkGetConstReferenceObjectMacro(FeatureMeans, FeatureValueVector); itkGetConstReferenceObjectMacro(FeatureStandardDeviations, FeatureValueVector); /** Set the desired feature set. Optional, for default value see above. */ itkSetConstObjectMacro(RequestedFeatures, FeatureNameVector); itkGetConstObjectMacro(RequestedFeatures, FeatureNameVector); /** Set the offsets over which the co-occurrence pairs will be computed. Optional; for default value see above. */ itkSetConstObjectMacro(Offsets, OffsetVector); itkGetConstObjectMacro(Offsets, OffsetVector); /** Set number of histogram bins along each axis. Optional; for default value see above. */ void SetNumberOfBinsPerAxis(unsigned int); /** Set the min and max (inclusive) pixel value that will be used for feature calculations. Optional; for default value see above. */ void SetPixelValueMinMax(PixelType min, PixelType max); /** Set the min and max (inclusive) pixel value that will be used for feature calculations. Optional; for default value see above. */ void SetDistanceValueMinMax( double min, double max ); /** Connects the mask image for which the histogram is going to be computed. Optional; for default value see above. */ void SetMaskImage(const ImageType *); const ImageType * GetMaskImage() const; /** Set the pixel value of the mask that should be considered "inside" the object. Optional; for default value see above. */ void SetInsidePixelValue(PixelType InsidePixelValue); itkGetConstMacro(FastCalculations, bool); itkSetMacro(FastCalculations, bool); itkBooleanMacro(FastCalculations); protected: - EnhancedScalarImageToRunLengthFeaturesFilter(); - ~EnhancedScalarImageToRunLengthFeaturesFilter() override {} - void PrintSelf( std::ostream & os, Indent indent ) const ITK_OVERRIDE; + EnhancedScalarImageToSizeZoneFeaturesFilter(); + virtual ~EnhancedScalarImageToSizeZoneFeaturesFilter() {} + virtual void PrintSelf( std::ostream & os, Indent indent ) const ITK_OVERRIDE; void FastCompute(); void FullCompute(); /** This method causes the filter to generate its output. */ - void GenerateData() ITK_OVERRIDE; + virtual void GenerateData() ITK_OVERRIDE; /** Make a DataObject to be used for output output. */ typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType; using Superclass::MakeOutput; - DataObjectPointer MakeOutput(DataObjectPointerArraySizeType) ITK_OVERRIDE; + virtual DataObjectPointer MakeOutput(DataObjectPointerArraySizeType) ITK_OVERRIDE; private: - typename RunLengthMatrixFilterType::Pointer m_RunLengthMatrixGenerator; + typename SizeZoneMatrixFilterType::Pointer m_SizeZoneMatrixGenerator; FeatureValueVectorPointer m_FeatureMeans; FeatureValueVectorPointer m_FeatureStandardDeviations; FeatureNameVectorConstPointer m_RequestedFeatures; OffsetVectorConstPointer m_Offsets; bool m_FastCalculations; }; } // end of namespace Statistics } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION -#include "itkEnhancedScalarImageToRunLengthFeaturesFilter.hxx" +#include "itkEnhancedScalarImageToSizeZoneFeaturesFilter.hxx" #endif #endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.hxx b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneFeaturesFilter.hxx similarity index 71% copy from Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.hxx copy to Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneFeaturesFilter.hxx index bda0fccfc2..13a0a2e58f 100644 --- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthFeaturesFilter.hxx +++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneFeaturesFilter.hxx @@ -1,410 +1,412 @@ /*=================================================================== 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. ===================================================================*/ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ -#ifndef __itkEnhancedScalarImageToRunLengthFeaturesFilter_hxx -#define __itkEnhancedScalarImageToRunLengthFeaturesFilter_hxx +#ifndef __itkEnhancedScalarImageToSizeZoneFeaturesFilter_hxx +#define __itkEnhancedScalarImageToSizeZoneFeaturesFilter_hxx -#include "itkEnhancedScalarImageToRunLengthFeaturesFilter.h" +#include "itkEnhancedScalarImageToSizeZoneFeaturesFilter.h" #include "itkNeighborhood.h" #include #include "vnl/vnl_math.h" namespace itk { namespace Statistics { template - EnhancedScalarImageToRunLengthFeaturesFilter - ::EnhancedScalarImageToRunLengthFeaturesFilter() + EnhancedScalarImageToSizeZoneFeaturesFilter + ::EnhancedScalarImageToSizeZoneFeaturesFilter() { this->SetNumberOfRequiredInputs( 1 ); this->SetNumberOfRequiredOutputs( 1 ); for( int i = 0; i < 2; ++i ) { this->ProcessObject::SetNthOutput( i, this->MakeOutput( i ) ); } - this->m_RunLengthMatrixGenerator = RunLengthMatrixFilterType::New(); + this->m_SizeZoneMatrixGenerator = SizeZoneMatrixFilterType::New(); this->m_FeatureMeans = FeatureValueVector::New(); this->m_FeatureStandardDeviations = FeatureValueVector::New(); // Set the requested features to the default value: // {Energy, Entropy, InverseDifferenceMoment, Inertia, ClusterShade, // ClusterProminence} FeatureNameVectorPointer requestedFeatures = FeatureNameVector::New(); // can't directly set this->m_RequestedFeatures since it is const! - requestedFeatures->push_back( RunLengthFeaturesFilterType::ShortRunEmphasis ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::LongRunEmphasis ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::GreyLevelNonuniformity ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::RunLengthNonuniformity ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::LowGreyLevelRunEmphasis ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::HighGreyLevelRunEmphasis ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::ShortRunLowGreyLevelEmphasis ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::ShortRunHighGreyLevelEmphasis ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::LongRunLowGreyLevelEmphasis ); - requestedFeatures->push_back( RunLengthFeaturesFilterType::LongRunHighGreyLevelEmphasis ); + requestedFeatures->push_back( SizeZoneFeaturesFilterType::SmallZoneEmphasis ); + requestedFeatures->push_back( SizeZoneFeaturesFilterType::LargeZoneEmphasis ); + requestedFeatures->push_back( SizeZoneFeaturesFilterType::GreyLevelNonuniformity ); + requestedFeatures->push_back( SizeZoneFeaturesFilterType::GreyLevelNonuniformityNormalized ); + requestedFeatures->push_back( SizeZoneFeaturesFilterType::SizeZoneNonuniformity ); + requestedFeatures->push_back( SizeZoneFeaturesFilterType::SizeZoneNonuniformityNormalized ); + requestedFeatures->push_back( SizeZoneFeaturesFilterType::LowGreyLevelZoneEmphasis ); + requestedFeatures->push_back( SizeZoneFeaturesFilterType::HighGreyLevelZoneEmphasis ); + requestedFeatures->push_back( SizeZoneFeaturesFilterType::SmallZoneLowGreyLevelEmphasis ); + requestedFeatures->push_back( SizeZoneFeaturesFilterType::SmallZoneHighGreyLevelEmphasis ); + requestedFeatures->push_back( SizeZoneFeaturesFilterType::LargeZoneLowGreyLevelEmphasis ); + requestedFeatures->push_back( SizeZoneFeaturesFilterType::LargeZoneHighGreyLevelEmphasis ); requestedFeatures->push_back( 20 ); this->SetRequestedFeatures( requestedFeatures ); // Set the offset directions to their defaults: half of all the possible // directions 1 pixel away. (The other half is included by symmetry.) // We use a neighborhood iterator to calculate the appropriate offsets. typedef Neighborhood NeighborhoodType; NeighborhoodType hood; hood.SetRadius( 1 ); // select all "previous" neighbors that are face+edge+vertex // connected to the current pixel. do not include the center pixel. unsigned int centerIndex = hood.GetCenterNeighborhoodIndex(); OffsetVectorPointer offsets = OffsetVector::New(); for( unsigned int d = 0; d < centerIndex; d++ ) { OffsetType offset = hood.GetOffset( d ); offsets->push_back( offset ); } this->SetOffsets( offsets ); this->m_FastCalculations = false; } template typename - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::DataObjectPointer - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::MakeOutput( DataObjectPointerArraySizeType itkNotUsed(idx) ) { return FeatureValueVectorDataObjectType::New().GetPointer(); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::GenerateData(void) { if ( this->m_FastCalculations ) { this->FastCompute(); } else { this->FullCompute(); } } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::FullCompute() { int numOffsets = this->m_Offsets->size(); int numFeatures = this->m_RequestedFeatures->size(); double **features; features = new double *[numOffsets]; for( int i = 0; i < numOffsets; i++ ) { features[i] = new double[numFeatures]; } unsigned long numberOfVoxels = 0; ImageRegionConstIterator voxelCountIter(this->GetMaskImage(),this->GetMaskImage()->GetLargestPossibleRegion()); while ( ! voxelCountIter.IsAtEnd() ) { if (voxelCountIter.Get() > 0) ++numberOfVoxels; ++voxelCountIter; } // For each offset, calculate each feature typename OffsetVector::ConstIterator offsetIt; int offsetNum, featureNum; - typedef typename RunLengthFeaturesFilterType::RunLengthFeatureName - InternalRunLengthFeatureName; + typedef typename SizeZoneFeaturesFilterType::SizeZoneFeatureName + InternalSizeZoneFeatureName; for( offsetIt = this->m_Offsets->Begin(), offsetNum = 0; offsetIt != this->m_Offsets->End(); offsetIt++, offsetNum++ ) { - this->m_RunLengthMatrixGenerator->SetOffset( offsetIt.Value() ); - this->m_RunLengthMatrixGenerator->Update(); - typename RunLengthFeaturesFilterType::Pointer runLengthMatrixCalculator = - RunLengthFeaturesFilterType::New(); - runLengthMatrixCalculator->SetInput( - this->m_RunLengthMatrixGenerator->GetOutput() ); - runLengthMatrixCalculator->SetNumberOfVoxels(numberOfVoxels); - runLengthMatrixCalculator->Update(); + this->m_SizeZoneMatrixGenerator->SetOffset( offsetIt.Value() ); + this->m_SizeZoneMatrixGenerator->Update(); + typename SizeZoneFeaturesFilterType::Pointer SizeZoneMatrixCalculator = + SizeZoneFeaturesFilterType::New(); + SizeZoneMatrixCalculator->SetInput( + this->m_SizeZoneMatrixGenerator->GetOutput() ); + SizeZoneMatrixCalculator->SetNumberOfVoxels(numberOfVoxels); + SizeZoneMatrixCalculator->Update(); typename FeatureNameVector::ConstIterator fnameIt; for( fnameIt = this->m_RequestedFeatures->Begin(), featureNum = 0; fnameIt != this->m_RequestedFeatures->End(); fnameIt++, featureNum++ ) { - features[offsetNum][featureNum] = runLengthMatrixCalculator->GetFeature( - ( InternalRunLengthFeatureName )fnameIt.Value() ); + features[offsetNum][featureNum] = SizeZoneMatrixCalculator->GetFeature( + ( InternalSizeZoneFeatureName )fnameIt.Value() ); } } // Now get the mean and deviaton of each feature across the offsets. this->m_FeatureMeans->clear(); this->m_FeatureStandardDeviations->clear(); double *tempFeatureMeans = new double[numFeatures]; double *tempFeatureDevs = new double[numFeatures]; /*Compute incremental mean and SD, a la Knuth, "The Art of Computer Programming, Volume 2: Seminumerical Algorithms", section 4.2.2. Compute mean and standard deviation using the recurrence relation: M(1) = x(1), M(k) = M(k-1) + (x(k) - M(k-1) ) / k S(1) = 0, S(k) = S(k-1) + (x(k) - M(k-1)) * (x(k) - M(k)) for 2 <= k <= n, then sigma = std::sqrt(S(n) / n) (or divide by n-1 for sample SD instead of population SD). */ // Set up the initial conditions (k = 1) for( featureNum = 0; featureNum < numFeatures; featureNum++ ) { tempFeatureMeans[featureNum] = features[0][featureNum]; tempFeatureDevs[featureNum] = 0; } - // Run through the recurrence (k = 2 ... N) + // Zone through the recurrence (k = 2 ... N) for( offsetNum = 1; offsetNum < numOffsets; offsetNum++ ) { int k = offsetNum + 1; for( featureNum = 0; featureNum < numFeatures; featureNum++ ) { double M_k_minus_1 = tempFeatureMeans[featureNum]; double S_k_minus_1 = tempFeatureDevs[featureNum]; double x_k = features[offsetNum][featureNum]; double M_k = M_k_minus_1 + ( x_k - M_k_minus_1 ) / k; double S_k = S_k_minus_1 + ( x_k - M_k_minus_1 ) * ( x_k - M_k ); tempFeatureMeans[featureNum] = M_k; tempFeatureDevs[featureNum] = S_k; } } for( featureNum = 0; featureNum < numFeatures; featureNum++ ) { tempFeatureDevs[featureNum] = std::sqrt( tempFeatureDevs[featureNum] / numOffsets ); this->m_FeatureMeans->push_back( tempFeatureMeans[featureNum] ); this->m_FeatureStandardDeviations->push_back( tempFeatureDevs[featureNum] ); } FeatureValueVectorDataObjectType *meanOutputObject = itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 0 ) ); meanOutputObject->Set( this->m_FeatureMeans ); FeatureValueVectorDataObjectType *standardDeviationOutputObject = itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 1 ) ); standardDeviationOutputObject->Set( this->m_FeatureStandardDeviations ); delete[] tempFeatureMeans; delete[] tempFeatureDevs; for( int i = 0; i < numOffsets; i++ ) { delete[] features[i]; } delete[] features; } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::FastCompute() { // Compute the feature for the first offset typename OffsetVector::ConstIterator offsetIt = this->m_Offsets->Begin(); - this->m_RunLengthMatrixGenerator->SetOffset( offsetIt.Value() ); + this->m_SizeZoneMatrixGenerator->SetOffset( offsetIt.Value() ); - this->m_RunLengthMatrixGenerator->Update(); - typename RunLengthFeaturesFilterType::Pointer runLengthMatrixCalculator = - RunLengthFeaturesFilterType::New(); - runLengthMatrixCalculator->SetInput( - this->m_RunLengthMatrixGenerator->GetOutput() ); - runLengthMatrixCalculator->Update(); + this->m_SizeZoneMatrixGenerator->Update(); + typename SizeZoneFeaturesFilterType::Pointer SizeZoneMatrixCalculator = + SizeZoneFeaturesFilterType::New(); + SizeZoneMatrixCalculator->SetInput( + this->m_SizeZoneMatrixGenerator->GetOutput() ); + SizeZoneMatrixCalculator->Update(); - typedef typename RunLengthFeaturesFilterType::RunLengthFeatureName - InternalRunLengthFeatureName; + typedef typename SizeZoneFeaturesFilterType::SizeZoneFeatureName + InternalSizeZoneFeatureName; this->m_FeatureMeans->clear(); this->m_FeatureStandardDeviations->clear(); typename FeatureNameVector::ConstIterator fnameIt; for( fnameIt = this->m_RequestedFeatures->Begin(); fnameIt != this->m_RequestedFeatures->End(); fnameIt++ ) { - this->m_FeatureMeans->push_back( runLengthMatrixCalculator->GetFeature( - ( InternalRunLengthFeatureName )fnameIt.Value() ) ); + this->m_FeatureMeans->push_back( SizeZoneMatrixCalculator->GetFeature( + ( InternalSizeZoneFeatureName )fnameIt.Value() ) ); this->m_FeatureStandardDeviations->push_back( 0.0 ); } FeatureValueVectorDataObjectType *meanOutputObject = itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 0 ) ); meanOutputObject->Set( this->m_FeatureMeans ); FeatureValueVectorDataObjectType *standardDeviationOutputObject = itkDynamicCastInDebugMode( this->ProcessObject::GetOutput( 1 ) ); standardDeviationOutputObject->Set( this->m_FeatureStandardDeviations ); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::SetInput( const ImageType *image ) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput( 0, const_cast( image ) ); - this->m_RunLengthMatrixGenerator->SetInput( image ); + this->m_SizeZoneMatrixGenerator->SetInput( image ); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::SetNumberOfBinsPerAxis( unsigned int numberOfBins ) { itkDebugMacro( "setting NumberOfBinsPerAxis to " << numberOfBins ); - this->m_RunLengthMatrixGenerator->SetNumberOfBinsPerAxis( numberOfBins ); + this->m_SizeZoneMatrixGenerator->SetNumberOfBinsPerAxis( numberOfBins ); this->Modified(); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::SetPixelValueMinMax( PixelType min, PixelType max ) { itkDebugMacro( "setting Min to " << min << "and Max to " << max ); - this->m_RunLengthMatrixGenerator->SetPixelValueMinMax( min, max ); + this->m_SizeZoneMatrixGenerator->SetPixelValueMinMax( min, max ); this->Modified(); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::SetDistanceValueMinMax( double min, double max ) { itkDebugMacro( "setting Min to " << min << "and Max to " << max ); - this->m_RunLengthMatrixGenerator->SetDistanceValueMinMax( min, max ); + this->m_SizeZoneMatrixGenerator->SetDistanceValueMinMax( min, max ); this->Modified(); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::SetMaskImage( const ImageType *image ) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput( 1, const_cast< ImageType * >( image ) ); - this->m_RunLengthMatrixGenerator->SetMaskImage( image ); + this->m_SizeZoneMatrixGenerator->SetMaskImage( image ); } template const TImage * - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::GetInput() const { if ( this->GetNumberOfInputs() < 1 ) { return ITK_NULLPTR; } return static_cast( this->ProcessObject::GetInput( 0 ) ); } template const typename - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::FeatureValueVectorDataObjectType * - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::GetFeatureMeansOutput() const { return itkDynamicCastInDebugMode (this->ProcessObject::GetOutput( 0 ) ); } template const typename - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::FeatureValueVectorDataObjectType * - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::GetFeatureStandardDeviationsOutput() const { return itkDynamicCastInDebugMode< const FeatureValueVectorDataObjectType * > ( this->ProcessObject::GetOutput( 1 ) ); } template const TImage * - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::GetMaskImage() const { if ( this->GetNumberOfInputs() < 2 ) { return ITK_NULLPTR; } return static_cast< const ImageType *>( this->ProcessObject::GetInput( 1 ) ); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::SetInsidePixelValue( PixelType insidePixelValue ) { itkDebugMacro( "setting InsidePixelValue to " << insidePixelValue ); - this->m_RunLengthMatrixGenerator->SetInsidePixelValue( insidePixelValue ); + this->m_SizeZoneMatrixGenerator->SetInsidePixelValue( insidePixelValue ); this->Modified(); } template void - EnhancedScalarImageToRunLengthFeaturesFilter + EnhancedScalarImageToSizeZoneFeaturesFilter ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "RequestedFeatures: " << this->GetRequestedFeatures() << std::endl; os << indent << "FeatureStandardDeviations: " << this->GetFeatureStandardDeviations() << std::endl; os << indent << "FastCalculations: " << this->GetFastCalculations() << std::endl; os << indent << "Offsets: " << this->GetOffsets() << std::endl; os << indent << "FeatureMeans: " << this->GetFeatureMeans() << std::endl; } } // end of namespace Statistics } // end of namespace itk -#endif \ No newline at end of file +#endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneMatrixFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneMatrixFilter.h new file mode 100644 index 0000000000..c0cf3f65f5 --- /dev/null +++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneMatrixFilter.h @@ -0,0 +1,283 @@ +/*=================================================================== + +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. + +===================================================================*/ + +/*========================================================================= +* +* Copyright Insight Software Consortium +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0.txt +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*=========================================================================*/ +#ifndef __itkEnhancedScalarImageToSizeZoneMatrixFilter_h +#define __itkEnhancedScalarImageToSizeZoneMatrixFilter_h + +#include "itkImage.h" +#include "itkHistogram.h" +#include "itkNumericTraits.h" +#include "itkVectorContainer.h" + +namespace itk +{ + namespace Statistics + { + /** \class EnhancedScalarImageToSizeZoneMatrixFilter + * \brief This class computes a run length matrix (histogram) from + * a given image and a mask image if provided. Run length matrces are + * used for image texture description. + * + * This filters creates a grey-level run length matrix from a N-D scalar + * image. This is another possible texture description. See the following + * references. + * M. M. Galloway. Texture analysis using gray level run lengths. Computer + * Graphics and Image Processing, 4:172-179, 1975. + * + * A. Chu, C. M. Sehgal, and J. F. Greenleaf. Use of gray value distribution of + * run lengths for texture analysis. Pattern Recognition Letters, 11:415-420, + * 1990. + * + * B. R. Dasarathy and E. B. Holder. Image characterizations based on joint + * gray-level run-length distributions. Pattern Recognition Letters, 12:490-502, + * 1991. + * + * The basic idea is as follows: + * Given an image and an offset (e.g. (1, -1) for a 2-d image), each element + * in the joint histogram describes the frequency for a particular distance/ + * intensity pair within a given image. This distance/intensity pair can be + * described as follows: we start at a given voxel which has some intensity. + * We then "jump" to neighboring pixels in increments provided by the offset(s) + * as long as the pixel to which we are jumping is within the same intensity + * bin as the original voxel. The distance component is given by the distance + * from the original to the final voxel satisfying our jumping criteria. + * + * The offset (or offsets) along which the co-occurences are calculated can be + * set by the user. Traditionally, only one offset is used per histogram, and + * offset components in the range [-1, 1] are used. For rotation-invariant + * features averages of features computed over several histograms with different + * offsets are generally used, instead of computing features from one histogram + * create with several offsets. Additionally, instead of using offsets of two or + * more pixels in any direction, multi-resolution techniques (e.g. image + * pyramids) are generally used to deal with texture at different spatial + * resolutions. + * + * This class calculates a 2-d histogram of all the intensity/distance pairs in + * the given image's requested region, for a given set of offsets. That is, if + * a given offset falls outside of the requested region (or outside the mask) + * at a particular point, that distance/intensity pair will not be added to + * the matrix. + * + * The number of histogram bins on each axis can be set (defaults to 256). Also, + * by default the histogram min and max corresponds to the largest and smallest + * possible pixel value of that pixel type. To customize the histogram bounds + * for a given image, the max and min pixel values that will be placed in the + * histogram can be set manually. NB: The min and max are INCLUSIVE. + * + * Further, the type of histogram frequency container used is an optional + * template parameter. By default, a dense container is used, but for images + * with little texture or in cases where the user wants more histogram bins, + * a sparse container can be used for the histogram instead. + * + * WARNING: This probably won't work for pixels of double or long-double type + * unless you set the histogram min and max manually. This is because the largest + * histogram bin by default has max value of the largest possible pixel value + * plus 1. For double and long-double types, whose "RealType" as defined by the + * NumericTraits class is the same, and thus cannot hold any larger values, + * this would cause a float overflow. + * + * IJ article: http://hdl.handle.net/1926/1374 + * + * \sa ScalarImageToSizeZoneFeaturesFilter + * \sa EnhancedScalarImageToSizeZoneMatrixFilter + * \sa HistogramToSizeZoneFeaturesFilter + * + * \author: Nick Tustison + * \ingroup ITKStatistics + */ + + template + class EnhancedScalarImageToSizeZoneMatrixFilter : public ProcessObject + { + public: + /** Standard typedefs */ + typedef EnhancedScalarImageToSizeZoneMatrixFilter Self; + typedef ProcessObject Superclass; + typedef SmartPointer Pointer; + typedef SmartPointer ConstPointer; + + /** Run-time type information (and related methods). */ + itkTypeMacro( EnhancedScalarImageToSizeZoneMatrixFilter, ProcessObject ); + + /** standard New() method support */ + itkNewMacro( Self ); + + typedef TImageType ImageType; + typedef typename ImageType::Pointer ImagePointer; + typedef typename ImageType::ConstPointer ImageConstPointer; + typedef typename ImageType::PixelType PixelType; + typedef typename ImageType::IndexType IndexType; + typedef typename ImageType::RegionType RegionType; + typedef typename ImageType::SizeType RadiusType; + typedef typename ImageType::OffsetType OffsetType; + typedef VectorContainer OffsetVector; + typedef typename OffsetVector::Pointer OffsetVectorPointer; + typedef typename ImageType::PointType PointType; + + typedef typename NumericTraits::RealType MeasurementType; + typedef typename NumericTraits::RealType RealType; + + typedef Histogram + HistogramType; + typedef typename HistogramType::Pointer HistogramPointer; + typedef typename HistogramType::ConstPointer HistogramConstPointer; + typedef typename HistogramType::MeasurementVectorType MeasurementVectorType; + + /** ImageDimension constants */ + itkStaticConstMacro( ImageDimension, unsigned int, + TImageType::ImageDimension ); + + /** Specify the default number of bins per axis */ + itkStaticConstMacro( DefaultBinsPerAxis, unsigned int, 256 ); + + /** + * Set the offsets over which the intensity/distance pairs will be computed. + * Invoking this function clears the previous offsets. + * Note: for each individual offset in the OffsetVector, the rightmost non-zero + * offset element must be positive. For example, in the offset list of a 2D image, + * (1, 0) means the offset along x-axis. (1, 0) has to be set instead + * of (-1, 0). This is required from the iterating order of pixel iterator. + * + */ + itkSetObjectMacro( Offsets, OffsetVector ); + + /** + * Set offset over which the intensity/distance pairs will be computed. + * Invoking this function clears the previous offset(s). + * Note: for each individual offset, the rightmost non-zero + * offset element must be positive. For example, in the offset list of a 2D image, + * (1, 0) means the offset along x-axis. (1, 0) has to be set instead + * of (-1, 0). This is required from the iterating order of pixel iterator. + * + */ + void SetOffset( const OffsetType offset ); + + /** + * Get the current offset(s). + */ + itkGetModifiableObjectMacro(Offsets, OffsetVector ); + + /** Set number of histogram bins along each axis */ + itkSetMacro( NumberOfBinsPerAxis, unsigned int ); + + /** Get number of histogram bins along each axis */ + itkGetConstMacro( NumberOfBinsPerAxis, unsigned int ); + + /** + * Set the min and max (inclusive) pixel value that will be used in + * generating the histogram. + */ + void SetPixelValueMinMax( PixelType min, PixelType max ); + + /** Get the min pixel value defining one dimension of the joint histogram. */ + itkGetConstMacro( Min, PixelType ); + + /** Get the max pixel value defining one dimension of the joint histogram. */ + itkGetConstMacro( Max, PixelType ); + + /** + * Set the min and max (inclusive) pixel value that will be used in + * generating the histogram. + */ + void SetDistanceValueMinMax( RealType min, RealType max ); + + /** + * Get the min distance value defining one dimension of the joint histogram. + */ + itkGetConstMacro( MinDistance, RealType ); + + /** + * Get the max distance value defining one dimension of the joint histogram. + */ + itkGetConstMacro( MaxDistance, RealType ); + + /** Method to set the input image */ + using Superclass::SetInput; + void SetInput( const ImageType *image ); + + /** Method to get the input image */ + const ImageType * GetInput() const; + + /** Method to set the mask image */ + void SetMaskImage( const ImageType *image ); + + /** Method to get the mask image */ + const ImageType * GetMaskImage() const; + + /** method to get the Histogram */ + const HistogramType * GetOutput() const; + + /** + * Set the pixel value of the mask that should be considered "inside" the + * object. Defaults to 1. + */ + itkSetMacro( InsidePixelValue, PixelType ); + itkGetConstMacro( InsidePixelValue, PixelType ); + + protected: + EnhancedScalarImageToSizeZoneMatrixFilter(); + virtual ~EnhancedScalarImageToSizeZoneMatrixFilter() {}; + virtual void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; + + /** Standard itk::ProcessObject subclass method. */ + typedef DataObject::Pointer DataObjectPointer; + + typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType; + using Superclass::MakeOutput; + virtual DataObjectPointer MakeOutput( DataObjectPointerArraySizeType idx ) ITK_OVERRIDE; + + /** This method causes the filter to generate its output. */ + virtual void GenerateData() ITK_OVERRIDE; + + private: + + unsigned int m_NumberOfBinsPerAxis; + PixelType m_Min; + PixelType m_Max; + RealType m_MinDistance; + RealType m_MaxDistance; + PixelType m_InsidePixelValue; + + MeasurementVectorType m_LowerBound; + MeasurementVectorType m_UpperBound; + OffsetVectorPointer m_Offsets; + }; + } // end of namespace Statistics +} // end of namespace itk + +#ifndef ITK_MANUAL_INSTANTIATION +#include "itkEnhancedScalarImageToSizeZoneMatrixFilter.hxx" +#endif + +#endif diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneMatrixFilter.hxx b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneMatrixFilter.hxx new file mode 100644 index 0000000000..1dcd78b09f --- /dev/null +++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneMatrixFilter.hxx @@ -0,0 +1,409 @@ +/*=================================================================== + +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. + +===================================================================*/ + +/*========================================================================= +* +* Copyright Insight Software Consortium +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0.txt +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*=========================================================================*/ +#ifndef __itkEnhancedScalarImageToSizeZoneMatrixFilter_hxx +#define __itkEnhancedScalarImageToSizeZoneMatrixFilter_hxx + +#include "itkEnhancedScalarImageToSizeZoneMatrixFilter.h" + +#include "itkConstNeighborhoodIterator.h" +#include "itkNeighborhood.h" +#include "vnl/vnl_math.h" +#include "itkMacro.h" +#include "itkRescaleIntensityImageFilter.h" +#include "itkMaskImageFilter.h" +#include "itkLabelStatisticsImageFilter.h" +#include "itkScalarConnectedComponentImageFilter.h" +#include "itkRelabelComponentImageFilter.h" +#include "itkCastImageFilter.h" + +#include + +namespace itk +{ +namespace Statistics +{ +template +EnhancedScalarImageToSizeZoneMatrixFilter +::EnhancedScalarImageToSizeZoneMatrixFilter() : + m_NumberOfBinsPerAxis( itkGetStaticConstMacro( DefaultBinsPerAxis ) ), + m_Min( NumericTraits::NonpositiveMin() ), + m_Max( NumericTraits::max() ), + m_MinDistance( NumericTraits::ZeroValue() ), + m_MaxDistance( NumericTraits::max() ), + m_InsidePixelValue( NumericTraits::OneValue() ) +{ + this->SetNumberOfRequiredInputs( 1 ); + this->SetNumberOfRequiredOutputs( 1 ); + + const unsigned int measurementVectorSize = 2; + + this->ProcessObject::SetNthOutput( 0, this->MakeOutput( 0 ) ); + HistogramType *output = const_cast( this->GetOutput() ); + output->SetMeasurementVectorSize( measurementVectorSize ); + + this->m_LowerBound.SetSize( measurementVectorSize ); + this->m_UpperBound.SetSize( measurementVectorSize ); + + this->m_LowerBound[0] = this->m_Min; + this->m_LowerBound[1] = this->m_MinDistance; + this->m_UpperBound[0] = this->m_Max; + this->m_UpperBound[1] = this->m_MaxDistance; +} + +template +void +EnhancedScalarImageToSizeZoneMatrixFilter +::SetOffset( const OffsetType offset ) +{ + OffsetVectorPointer offsetVector = OffsetVector::New(); + offsetVector->push_back( offset ); + this->SetOffsets( offsetVector ); +} + +template +void +EnhancedScalarImageToSizeZoneMatrixFilter +::SetInput( const ImageType *image ) +{ + // Process object is not const-correct so the const_cast is required here + this->ProcessObject::SetNthInput( 0, const_cast( image ) ); +} + +template +void +EnhancedScalarImageToSizeZoneMatrixFilter +::SetMaskImage( const ImageType *image ) +{ + // Process object is not const-correct so the const_cast is required here + this->ProcessObject::SetNthInput( 1, const_cast( image ) ); +} + +template +const TImageType * +EnhancedScalarImageToSizeZoneMatrixFilter +::GetInput() const +{ + if( this->GetNumberOfInputs() < 1 ) + { + return ITK_NULLPTR; + } + return static_cast( this->ProcessObject::GetInput( 0 ) ); +} + +template +const TImageType * +EnhancedScalarImageToSizeZoneMatrixFilter +::GetMaskImage() const +{ + if( this->GetNumberOfInputs() < 2 ) + { + return ITK_NULLPTR; + } + return static_cast( this->ProcessObject::GetInput( 1 ) ); +} + +template +const typename EnhancedScalarImageToSizeZoneMatrixFilter::HistogramType * +EnhancedScalarImageToSizeZoneMatrixFilter +::GetOutput() const +{ + const HistogramType *output = + static_cast( this->ProcessObject::GetOutput( 0 ) ); + return output; +} + +template +typename EnhancedScalarImageToSizeZoneMatrixFilter::DataObjectPointer +EnhancedScalarImageToSizeZoneMatrixFilter +::MakeOutput( DataObjectPointerArraySizeType itkNotUsed( idx ) ) +{ + return HistogramType::New().GetPointer(); +} + +template +void +EnhancedScalarImageToSizeZoneMatrixFilter +::GenerateData() +{ + HistogramType *output = + static_cast( this->ProcessObject::GetOutput( 0 ) ); + + const ImageType * inputImage = this->GetInput(); + const ImageType * maskImage = this->GetMaskImage(); + + // First, create an appropriate histogram with the right number of bins + // and mins and maxes correct for the image type. + typename HistogramType::SizeType size( output->GetMeasurementVectorSize() ); + + size.Fill( this->m_NumberOfBinsPerAxis ); + this->m_LowerBound[0] = this->m_Min; + this->m_LowerBound[1] = this->m_MinDistance; + this->m_UpperBound[0] = this->m_Max; + this->m_UpperBound[1] = this->m_MaxDistance; + + output->Initialize( size, this->m_LowerBound, this->m_UpperBound ); + + MeasurementVectorType run( output->GetMeasurementVectorSize() ); + typename HistogramType::IndexType hIndex; + + //Cast the image to a float image - with no respect to the incoming image + //to prevent some non-templated itk issues + typedef itk::Image FloatImageType; + typedef itk::CastImageFilter CastFilterType; + + typename CastFilterType::Pointer caster = CastFilterType::New(); + caster->SetInput(inputImage); + caster->Update(); + typename FloatImageType::Pointer floatImage = caster->GetOutput(); + + //MITK_WARN << "InputImage casted."; + + //Cast the mask to an unsigned short image - with no respect to the incomimg maskimage + //to prevent some non-templated itk issues + typedef unsigned short LabelPixelType; + typedef itk::Image LabelImageType; + + typedef itk::CastImageFilter MaskCastFilterType; + typename MaskCastFilterType::Pointer maskCaster = MaskCastFilterType::New(); + maskCaster->SetInput(maskImage); + maskCaster->Update(); + + //MITK_WARN << "MaskImage casted."; + + //Set all values out of the mask to (m_Min + m_Max) / 2. + typedef itk::MaskImageFilter< FloatImageType, LabelImageType, FloatImageType > MaskFilterType; + typename MaskFilterType::Pointer maskFilter = MaskFilterType::New(); + maskFilter->SetInput(floatImage); + maskFilter->SetMaskImage(maskCaster->GetOutput()); + maskFilter->SetOutsideValue((m_Max + m_Min) / 2); + maskFilter->Update(); + + //MITK_WARN << "InputImage masked."; + + //Rescale intensity to match the size of the histogram + typedef itk::Image< unsigned int, 3 > OutputImageType; + + typedef itk::RescaleIntensityImageFilter< FloatImageType,OutputImageType> RescalerType; + typename RescalerType::Pointer rescaler = RescalerType::New(); + //We use 0 for nans, all valid numbers will be 1 < x < size + rescaler->SetOutputMinimum( 1 ); + rescaler->SetOutputMaximum( size[0] ); + rescaler->SetInput(maskFilter->GetOutput()); + rescaler->Update(); + + typename OutputImageType::Pointer rescaled = rescaler->GetOutput(); + + //MITK_WARN << "Intensities rescaled."; + + //Write back the nans because they get lost during rescaling + + int xx = inputImage->GetLargestPossibleRegion().GetSize()[0]; + int yy = inputImage->GetLargestPossibleRegion().GetSize()[1]; + int zz = inputImage->GetLargestPossibleRegion().GetSize()[2]; + + for (int x = 0; x < xx; x++) + { + for (int y = 0; y < yy; y++) + { + for (int z = 0; z < zz; z++) + { + FloatImageType::IndexType indexF; + indexF[0] = x; + indexF[1] = y; + indexF[2] = z; + + OutputImageType::IndexType indexO; + indexO[0] = x; + indexO[1] = y; + indexO[2] = z; + + //Is Pixel NaN? + if(floatImage->GetPixel(indexF) != floatImage->GetPixel(indexF)) + { + rescaled->SetPixel(indexO,0); + } + } + } + } + //All nans are now 0, the valid values are within [1,numberOfBins] + + /* + OutputImageType::IndexType indexO; + indexO[0] = 0; + indexO[1] = 2; + indexO[2] = 1; + MITK_WARN << "is 0: " << rescaled->GetPixel(indexO); + indexO[0] = 0; + indexO[1] = 0; + indexO[2] = 0; + MITK_WARN << "is 1: " << rescaled->GetPixel(indexO); + */ + + PixelType distanceThreshold = 1 - mitk::eps; + + + //Calculate the connected components + typedef itk::ScalarConnectedComponentImageFilter + ConnectedComponentImageFilterType; + + typename ConnectedComponentImageFilterType::Pointer connected = ConnectedComponentImageFilterType::New (); + connected->SetInput(rescaled); + connected->SetMaskImage(maskCaster->GetOutput()); + connected->SetDistanceThreshold(distanceThreshold); + connected->Update(); + + /* + indexO[0] = 0; + indexO[1] = 2; + indexO[2] = 1; + MITK_WARN << "is 0: " << (connected->GetOutput())->GetPixel(indexO); + indexO[0] = 0; + indexO[1] = 0; + indexO[2] = 0; + MITK_WARN << "is 1: " << (connected->GetOutput())->GetPixel(indexO); + + MITK_WARN << "Connected components calculated."; + */ + + //Relabel the components + typedef itk::RelabelComponentImageFilter RelabelFilterType; + typename RelabelFilterType::Pointer relabel = RelabelFilterType::New(); + + typename RelabelFilterType::ObjectSizeType minSize = 1; + + relabel->SetInput(connected->GetOutput()); + relabel->SetMinimumObjectSize(minSize); + relabel->Update(); + + //MITK_WARN << "Components relabeled."; + + //Get the stats of the componentes + typedef itk::LabelStatisticsImageFilter< FloatImageType, OutputImageType> LabelStatisticsImageFilterType; + typename LabelStatisticsImageFilterType::Pointer labelStatisticsImageFilter = + LabelStatisticsImageFilterType::New(); + labelStatisticsImageFilter->SetLabelInput( relabel->GetOutput() ); + labelStatisticsImageFilter->SetInput(floatImage); + labelStatisticsImageFilter->UseHistogramsOn(); // needed to compute median + labelStatisticsImageFilter->Update(); + + /* + std::cout << "Number of labels: " + << labelStatisticsImageFilter->GetNumberOfLabels() << std::endl; + std::cout << std::endl; + */ + typedef typename LabelStatisticsImageFilterType::ValidLabelValuesContainerType ValidLabelValuesType; + + for(typename ValidLabelValuesType::const_iterator vIt = labelStatisticsImageFilter->GetValidLabelValues().begin(); + vIt != labelStatisticsImageFilter->GetValidLabelValues().end(); + ++vIt) + { + if ( labelStatisticsImageFilter->HasLabel(*vIt) ) + { + LabelPixelType labelValue = *vIt; + + run[0] = labelStatisticsImageFilter->GetMean( labelValue ); + run[1] = labelStatisticsImageFilter->GetCount( labelValue ); + + //Check for NaN and inf + if(run[0] == run[0] && !std::isinf(std::abs(run[0]))) + { + output->GetIndex( run, hIndex ); + output->IncreaseFrequencyOfIndex( hIndex, 1 ); + + /* + MITK_INFO << "Adding a region:"; + MITK_INFO << "\tmin: " + << labelStatisticsImageFilter->GetMinimum( labelValue ); + MITK_INFO << "\tmax: " + << labelStatisticsImageFilter->GetMaximum( labelValue ); + MITK_INFO << "\tmean: " + << labelStatisticsImageFilter->GetMean( labelValue ); + MITK_INFO << "\tcount: " + << labelStatisticsImageFilter->GetCount( labelValue ); + */ + } + } + } +} + +template +void +EnhancedScalarImageToSizeZoneMatrixFilter +::SetPixelValueMinMax( PixelType min, PixelType max ) +{ + if( this->m_Min != min || this->m_Max != max ) + { + itkDebugMacro( "setting Min to " << min << "and Max to " << max ); + this->m_Min = min; + this->m_Max = max; + this->Modified(); + } +} + +template +void +EnhancedScalarImageToSizeZoneMatrixFilter +::SetDistanceValueMinMax( RealType min, RealType max ) +{ + if( this->m_MinDistance != min || this->m_MaxDistance != max ) + { + itkDebugMacro( "setting MinDistance to " << min << "and MaxDistance to " + << max ); + this->m_MinDistance = min; + this->m_MaxDistance = max; + this->Modified(); + } +} + +template +void +EnhancedScalarImageToSizeZoneMatrixFilter +::PrintSelf( std::ostream& os, Indent indent ) const +{ + Superclass::PrintSelf( os,indent ); + os << indent << "Offsets: " << this->GetOffsets() << std::endl; + os << indent << "Min: " << this->m_Min << std::endl; + os << indent << "Max: " << this->m_Max << std::endl; + os << indent << "Min distance: " << this->m_MinDistance << std::endl; + os << indent << "Max distance: " << this->m_MaxDistance << std::endl; + os << indent << "NumberOfBinsPerAxis: " << this->m_NumberOfBinsPerAxis + << std::endl; + os << indent << "InsidePixelValue: " << this->m_InsidePixelValue << std::endl; +} +} // end of namespace Statistics +} // end of namespace itk + +#endif diff --git a/Modules/Classification/CLUtilities/include/itkLocalStatisticFilter.h b/Modules/Classification/CLUtilities/include/itkLocalStatisticFilter.h new file mode 100644 index 0000000000..4ff22c4e54 --- /dev/null +++ b/Modules/Classification/CLUtilities/include/itkLocalStatisticFilter.h @@ -0,0 +1,51 @@ +#ifndef itkLocalStatisticFilter_h +#define itkLocalStatisticFilter_h + +#include "itkImageToImageFilter.h" + +namespace itk +{ + template + class LocalStatisticFilter : public ImageToImageFilter< TInputImageType, TOuputImageType> + { + public: + typedef LocalStatisticFilter Self; + typedef ImageToImageFilter< TInputImageType, TOuputImageType > Superclass; + typedef SmartPointer< Self > Pointer; + typedef typename TInputImageType::ConstPointer InputImagePointer; + typedef typename TOuputImageType::Pointer OutputImagePointer; + typedef typename TOuputImageType::RegionType OutputImageRegionType; + + itkNewMacro (Self); + itkTypeMacro(LocalStatisticFilter, ImageToImageFilter); + + itkSetMacro(Size, int); + itkGetConstMacro(Size, int); + + protected: + LocalStatisticFilter(); + ~LocalStatisticFilter(){}; + + virtual void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId); + virtual void BeforeThreadedGenerateData(void); + + + using itk::ProcessObject::MakeOutput; + virtual itk::ProcessObject::DataObjectPointer MakeOutput(itk::ProcessObject::DataObjectPointerArraySizeType /*idx*/) override; + + void CreateOutputImage(InputImagePointer input, OutputImagePointer output); + + private: + LocalStatisticFilter(const Self &); // purposely not implemented + void operator=(const Self &); // purposely not implemented + + int m_Size; + int m_Bins; + }; +} + +#ifndef ITK_MANUAL_INSTANTIATION +#include "itkLocalStatisticFilter.hxx" +#endif + +#endif // itkLocalStatisticFilter_h diff --git a/Modules/Classification/CLUtilities/include/itkLocalStatisticFilter.hxx b/Modules/Classification/CLUtilities/include/itkLocalStatisticFilter.hxx new file mode 100644 index 0000000000..a8fabc92fe --- /dev/null +++ b/Modules/Classification/CLUtilities/include/itkLocalStatisticFilter.hxx @@ -0,0 +1,113 @@ +#ifndef itkLocalStatisticFilter_cpp +#define itkLocalStatisticFilter_cpp + +#include + +#include +#include +#include +#include "itkMinimumMaximumImageCalculator.h" + +#include + +template< class TInputImageType, class TOuputImageType> +itk::LocalStatisticFilter::LocalStatisticFilter(): + m_Size(5), m_Bins(5) +{ + this->SetNumberOfRequiredOutputs(m_Bins); + this->SetNumberOfRequiredInputs(0); + + for (int i = 0; i < m_Bins; ++i) + { + this->SetNthOutput( i, this->MakeOutput(i) ); + } +} + +template< class TInputImageType, class TOuputImageType> +void +itk::LocalStatisticFilter::BeforeThreadedGenerateData() +{ + InputImagePointer input = this->GetInput(0); + for (int i = 0; i < m_Bins; ++i) + { + CreateOutputImage(input, this->GetOutput(i)); + } +} + +template< class TInputImageType, class TOuputImageType> +void +itk::LocalStatisticFilter::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType /*threadId*/) +{ + typedef itk::ImageRegionIterator IteratorType; + typedef itk::ConstNeighborhoodIterator ConstIteratorType; + + typename TInputImageType::SizeType size; size.Fill(m_Size); + InputImagePointer input = this->GetInput(0); + + if (TInputImageType::ImageDimension == 3) + { + size[2] = 0; + } + +// MITK_INFO << "Creating output iterator"; + std::vector iterVector; + for (int i = 0; i < m_Bins; ++i) + { + IteratorType iter(this->GetOutput(i), outputRegionForThread); + iterVector.push_back(iter); + } + + ConstIteratorType inputIter(size, input, outputRegionForThread); + while (!inputIter.IsAtEnd()) + { + for (int i = 0; i < m_Bins; ++i) + { + iterVector[i].Set(0); + } + + double min = std::numeric_limits::max(); + double max = std::numeric_limits::lowest(); + double mean = 0; + double std = 0; + + for (unsigned int i = 0; i < inputIter.Size(); ++i) + { + double value = inputIter.GetPixel(i); + min = std::min(min, value); + max = std::max(max, value); + mean += value / inputIter.Size(); + std += (value*value) / inputIter.Size(); + } + + iterVector[0].Value() = min; + iterVector[1].Value() = max; + iterVector[2].Value() = mean; + iterVector[3].Value() = std::sqrt(std - mean*mean); + iterVector[4].Value() = max-min; + + for (int i = 0; i < m_Bins; ++i) + { + ++(iterVector[i]); + } + ++inputIter; + } +} + +template< class TInputImageType, class TOuputImageType> +itk::ProcessObject::DataObjectPointer + itk::LocalStatisticFilter::MakeOutput(itk::ProcessObject::DataObjectPointerArraySizeType /*idx*/) +{ + itk::ProcessObject::DataObjectPointer output; + output = ( TOuputImageType::New() ).GetPointer(); + return output; +} + +template< class TInputImageType, class TOuputImageType> +void + itk::LocalStatisticFilter::CreateOutputImage(InputImagePointer input, OutputImagePointer output) +{ + output->SetRegions(input->GetLargestPossibleRegion()); + output->Allocate(); +} + +#endif //itkLocalStatisticFilter_cpp diff --git a/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp b/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp index 159d92aef8..a51d6253c5 100644 --- a/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp +++ b/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp @@ -1,96 +1,112 @@ #ifndef itkMultiHistogramFilter_cpp #define itkMultiHistogramFilter_cpp #include #include +#include #include +#include "itkMinimumMaximumImageCalculator.h" template< class TInputImageType, class TOuputImageType> itk::MultiHistogramFilter::MultiHistogramFilter(): - m_Delta(0.6), m_Offset(-3.0) +m_Delta(0.6), m_Offset(-3.0), m_Bins(11), m_Size(5), m_UseImageIntensityRange(false) { - this->SetNumberOfRequiredOutputs(11); + this->SetNumberOfRequiredOutputs(m_Bins); this->SetNumberOfRequiredInputs(0); - for (unsigned int i = 0; i < 11; ++i) + for (int i = 0; i < m_Bins; ++i) { this->SetNthOutput( i, this->MakeOutput(i) ); } } template< class TInputImageType, class TOuputImageType> void - itk::MultiHistogramFilter::GenerateData() +itk::MultiHistogramFilter::BeforeThreadedGenerateData() +{ + typedef itk::MinimumMaximumImageCalculator + ImageCalculatorFilterType; + + if (m_UseImageIntensityRange) + { + typename ImageCalculatorFilterType::Pointer imageCalculatorFilter + = ImageCalculatorFilterType::New(); + imageCalculatorFilter->SetImage(this->GetInput(0)); + imageCalculatorFilter->Compute(); + + m_Offset = imageCalculatorFilter->GetMinimum(); + m_Delta = 1.0*(imageCalculatorFilter->GetMaximum() - imageCalculatorFilter->GetMinimum()) / (1.0*m_Bins); + } + + InputImagePointer input = this->GetInput(0); + for (int i = 0; i < m_Bins; ++i) + { + CreateOutputImage(input, this->GetOutput(i)); + } +} +template< class TInputImageType, class TOuputImageType> +void +itk::MultiHistogramFilter::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType /*threadId*/) { double offset = m_Offset;// -3.0; double delta = m_Delta;// 0.6; - typedef itk::NeighborhoodIterator IteratorType; + typedef itk::ImageRegionIterator IteratorType; typedef itk::ConstNeighborhoodIterator ConstIteratorType; + typename TInputImageType::SizeType size; size.Fill(m_Size); InputImagePointer input = this->GetInput(0); - CreateOutputImage(input, this->GetOutput(0)); - CreateOutputImage(input, this->GetOutput(1)); - CreateOutputImage(input, this->GetOutput(2)); - CreateOutputImage(input, this->GetOutput(3)); - CreateOutputImage(input, this->GetOutput(4)); - CreateOutputImage(input, this->GetOutput(5)); - CreateOutputImage(input, this->GetOutput(6)); - CreateOutputImage(input, this->GetOutput(7)); - CreateOutputImage(input, this->GetOutput(8)); - CreateOutputImage(input, this->GetOutput(9)); - CreateOutputImage(input, this->GetOutput(10)); - - typename TInputImageType::SizeType size; size.Fill(5); + +// MITK_INFO << "Creating output iterator"; std::vector iterVector; - for (int i = 0; i < 11; ++i) + for (int i = 0; i < m_Bins; ++i) { - IteratorType iter(size, this->GetOutput(i), this->GetOutput(i)->GetLargestPossibleRegion()); + IteratorType iter(this->GetOutput(i), outputRegionForThread); iterVector.push_back(iter); } - ConstIteratorType inputIter( size, input, input->GetLargestPossibleRegion()); + ConstIteratorType inputIter(size, input, outputRegionForThread); while (!inputIter.IsAtEnd()) { - for (unsigned int i = 0; i < 11; ++i) + for (int i = 0; i < m_Bins; ++i) { - iterVector[i].SetCenterPixel(0); + iterVector[i].Set(0); } for (unsigned int i = 0; i < inputIter.Size(); ++i) { double value = inputIter.GetPixel(i); value -= offset; value /= delta; auto pos = (int)(value); - pos = std::max(0, std::min(10, pos)); - iterVector[pos].SetCenterPixel(iterVector[pos].GetCenterPixel() + 1); + pos = std::max(0, std::min(m_Bins-1, pos)); + iterVector[pos].Value() += 1;// (iterVector[pos].GetCenterPixel() + 1); } - for (unsigned int i = 0; i < 11; ++i) + for (int i = 0; i < m_Bins; ++i) { ++(iterVector[i]); } ++inputIter; } } template< class TInputImageType, class TOuputImageType> itk::ProcessObject::DataObjectPointer itk::MultiHistogramFilter::MakeOutput(itk::ProcessObject::DataObjectPointerArraySizeType /*idx*/) { itk::ProcessObject::DataObjectPointer output; output = ( TOuputImageType::New() ).GetPointer(); return output; } template< class TInputImageType, class TOuputImageType> void itk::MultiHistogramFilter::CreateOutputImage(InputImagePointer input, OutputImagePointer output) { output->SetRegions(input->GetLargestPossibleRegion()); output->Allocate(); } #endif //itkMultiHistogramFilter_cpp diff --git a/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.h b/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.h index 1b0a4f9307..8e5e41d396 100644 --- a/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.h +++ b/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.h @@ -1,51 +1,66 @@ #ifndef itkMultiHistogramFilter_h #define itkMultiHistogramFilter_h #include "itkImageToImageFilter.h" namespace itk { template class MultiHistogramFilter : public ImageToImageFilter< TInputImageType, TOuputImageType> { public: typedef MultiHistogramFilter Self; typedef ImageToImageFilter< TInputImageType, TOuputImageType > Superclass; typedef SmartPointer< Self > Pointer; typedef typename TInputImageType::ConstPointer InputImagePointer; typedef typename TOuputImageType::Pointer OutputImagePointer; + typedef typename TOuputImageType::RegionType OutputImageRegionType; itkNewMacro (Self); itkTypeMacro(MultiHistogramFilter, ImageToImageFilter); itkSetMacro(Delta, double); itkGetConstMacro(Delta, double); itkSetMacro(Offset, double); itkGetConstMacro(Offset, double); + itkSetMacro(Bins, int); + itkGetConstMacro(Bins, int); + + itkSetMacro(Size, int); + itkGetConstMacro(Size, int); + + itkSetMacro(UseImageIntensityRange, bool); + itkGetConstMacro(UseImageIntensityRange, bool); + protected: MultiHistogramFilter(); ~MultiHistogramFilter(){}; - virtual void GenerateData(); + virtual void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId); + virtual void BeforeThreadedGenerateData(void); + using itk::ProcessObject::MakeOutput; itk::ProcessObject::DataObjectPointer MakeOutput(itk::ProcessObject::DataObjectPointerArraySizeType /*idx*/) override; void CreateOutputImage(InputImagePointer input, OutputImagePointer output); private: MultiHistogramFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented double m_Delta; double m_Offset; + int m_Bins; + int m_Size; + bool m_UseImageIntensityRange; }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkMultiHistogramFilter.cpp" #endif #endif // itkMultiHistogramFilter_h diff --git a/Modules/Classification/CLUtilities/include/mitkCLResultWritter.h b/Modules/Classification/CLUtilities/include/mitkCLResultWritter.h new file mode 100644 index 0000000000..4d813247c6 --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkCLResultWritter.h @@ -0,0 +1,63 @@ +/*=================================================================== + +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 mitkCLResultWritter_h +#define mitkCLResultWritter_h + +#include "MitkCLUtilitiesExports.h" + +#include +#include +#include + +#include + +namespace mitk +{ + namespace cl + { + class MITKCLUTILITIES_EXPORT FeatureResultWritter + { + public: + FeatureResultWritter(std::string, int mode); + ~FeatureResultWritter(); + + void SetDecimalPoint(char decimal); + + void AddSubjectInformation(std::string value); + void AddColumn(std::string value); + void AddColumn(double value); + void NewRow(std::string endName); + + void AddResult(std::string desc, int slice, mitk::AbstractGlobalImageFeature::FeatureListType stats, bool , bool withDescription); + void AddHeader(std::string, int slice, mitk::AbstractGlobalImageFeature::FeatureListType stats, bool withHeader, bool withDescription); + + private: + int m_Mode; + std::size_t m_CurrentRow; + int m_CurrentElement; + std::string m_Separator; + std::ofstream m_Output; + std::vector m_List; + std::string m_SubjectInformation; + bool m_UsedSubjectInformation; + bool m_UseSpecialDecimalPoint; + char m_DecimalPoint; + }; + } +} + +#endif //mitkCLResultWritter_h \ No newline at end of file diff --git a/Modules/Classification/CLUtilities/include/mitkCLUtil.h b/Modules/Classification/CLUtilities/include/mitkCLUtil.h index 868ecd6069..6396455675 100644 --- a/Modules/Classification/CLUtilities/include/mitkCLUtil.h +++ b/Modules/Classification/CLUtilities/include/mitkCLUtil.h @@ -1,526 +1,571 @@ #ifndef mitkCLUtil_h #define mitkCLUtil_h #include #include #include #include #include #include #include namespace mitk { class MITKCLUTILITIES_EXPORT CLUtil { public: /// /// \brief The MorphologicalDimensions enum /// enum MorphologicalDimensions { Axial,Coronal,Sagital,All }; /// /// \brief CreateCheckerBoardPredictionMask /// \param image /// \param outimage /// static void CreateCheckerboardMask(mitk::Image::Pointer image, mitk::Image::Pointer & outimage); /// /// \brief InterpolateCreateCheckerboardPrediction /// \param image /// \param outimage /// static void InterpolateCheckerboardPrediction(mitk::Image::Pointer checkerboard_prediction, mitk::Image::Pointer & checkerboard_mask, mitk::Image::Pointer & outimage); /// /// \brief CountVoxel /// \param image /// \param map /// static void CountVoxel(mitk::Image::Pointer image, std::map & map); /// /// \brief CountVoxel /// \param image /// \param label /// \param count /// static void CountVoxel(mitk::Image::Pointer image, unsigned int label, unsigned int & count); /// /// \brief CountVoxel /// \param image /// \param count /// static void CountVoxel(mitk::Image::Pointer image, unsigned int & count); /// /// \brief SumVoxelForLabel /// \param image /// \param source /// \param label /// \param val /// static void SumVoxelForLabel(mitk::Image::Pointer image, const mitk::Image::Pointer & source , unsigned int label, double & val ); /// /// \brief SqSumVoxelForLabel /// \param image /// \param source /// \param label /// \param val /// static void SqSumVoxelForLabel(mitk::Image::Pointer image, const mitk::Image::Pointer & source, unsigned int label, double & val ); /// /// \brief LogicalAndImages /// \param image1 /// \param image2 /// static void LogicalAndImages(const Image::Pointer &image1, const Image::Pointer &image2, Image::Pointer &outimage); /// /// \brief GaussianFilter /// \param image /// \param smoothed /// \param sigma /// static void GaussianFilter(mitk::Image::Pointer image, mitk::Image::Pointer & smoothed ,double sigma); + /// + /// \brief SubtractGaussianFilter + /// \param image + /// \param smoothed (Result is sigma1-sigma2) + /// \param sigma1 + /// \param sigma2 + /// + static void DifferenceOfGaussianFilter(mitk::Image::Pointer image, mitk::Image::Pointer & smoothed, double sigma1, double sigma2); + + /// + /// \brief Laplacian of Gaussian + /// \param image + /// \param smoothed (Result is sigma1-sigma2) + /// \param sigma1 + /// \param sigma2 + /// + static void LaplacianOfGaussianFilter(mitk::Image::Pointer image, mitk::Image::Pointer & smoothed, double sigma1); + + /// + /// \brief SubtractGaussianFilter + /// \param image + /// \param smoothed (Result is sigma1-sigma2) + /// \param sigma1 + /// \param sigma2 + /// + static void HessianOfGaussianFilter(mitk::Image::Pointer image, std::vector &out, double sigma); + + /// + /// \brief Local Histogram + /// \param image + /// \param smoothed (Result is sigma1-sigma2) + /// \param sigma1 + /// \param sigma2 + /// + static void LocalHistogram(mitk::Image::Pointer image, std::vector &out, int Bins, int NeighbourhoodSize); + /// /// \brief transform /// \param matrix /// \param mask /// \param outimage /// template static mitk::Image::Pointer Transform(const Eigen::Matrix & matrix, const mitk::Image::Pointer & mask) { itk::Image::Pointer itkMask; mitk::CastToItkImage(mask,itkMask); typename itk::Image::Pointer itk_img = itk::Image::New(); itk_img->SetRegions(itkMask->GetLargestPossibleRegion()); itk_img->SetOrigin(itkMask->GetOrigin()); itk_img->SetSpacing(itkMask->GetSpacing()); itk_img->SetDirection(itkMask->GetDirection()); itk_img->Allocate(); unsigned int n_numSamples = 0; mitk::CLUtil::CountVoxel(mask,n_numSamples); if(n_numSamples != matrix.rows()) MITK_ERROR << "Number of samples in matrix and number of points under the masks is not the same!"; auto mit = itk::ImageRegionConstIterator >(itkMask, itkMask->GetLargestPossibleRegion()); auto oit = itk::ImageRegionIterator >(itk_img, itk_img->GetLargestPossibleRegion()); unsigned int current_row = 0; while(!mit.IsAtEnd()) { if(mit.Value() > 0) oit.Set(matrix(current_row++,0)); else oit.Set(0.0); ++mit; ++oit; } mitk::Image::Pointer out_img = mitk::Image::New(); mitk::GrabItkImageMemory(itk_img,out_img); return out_img; } /// /// \brief TransformImageToMatrix /// \param in_img /// \param mask /// \param out_matrix /// template static Eigen::Matrix Transform(const mitk::Image::Pointer & img, const mitk::Image::Pointer & mask) { itk::Image::Pointer current_mask; mitk::CastToItkImage(mask,current_mask); unsigned int n_numSamples = 0; mitk::CLUtil::CountVoxel(mask,n_numSamples); typename itk::Image::Pointer current_img; mitk::CastToItkImage(img,current_img); Eigen::Matrix out_matrix(n_numSamples,1); auto mit = itk::ImageRegionConstIterator >(current_mask, current_mask->GetLargestPossibleRegion()); auto iit = itk::ImageRegionConstIterator >(current_img,current_img->GetLargestPossibleRegion()); unsigned int current_row = 0; while (!mit.IsAtEnd()) { if(mit.Value() > 0) out_matrix(current_row++) = iit.Value(); ++mit; ++iit; } return out_matrix; } /// /// \brief DilateBinary /// \param BinaryImage /// \param BinaryImage /// \param Size of the StructuringElement /// \param Dimension /// static void DilateBinary(mitk::Image::Pointer & sourceImage, mitk::Image::Pointer& resultImage, int radius , MorphologicalDimensions d); /// /// \brief ErodeBinary /// \param BinaryImage /// \param BinaryImage /// \param Size of the StructuringElement /// \param Dimension /// static void ErodeBinary(mitk::Image::Pointer & sourceImage, mitk::Image::Pointer& resultImage, int radius, MorphologicalDimensions d); /// /// \brief ClosingBinary /// \param BinaryImage /// \param BinaryImage /// \param Size of the StructuringElement /// \param Dimension /// static void ClosingBinary(mitk::Image::Pointer & sourceImage, mitk::Image::Pointer& resultImage, int radius, MorphologicalDimensions d); /// /// \brief MergeLabels /// \param MultilabelImage /// \param map merge instruction where each map entry defines a mapping instruction. Key - Value /// static void MergeLabels(mitk::Image::Pointer & img, const std::map & map); /// /// \brief ConnectedComponentsImage /// \param BinaryImage /// \param BinaryImage /// \param MultilabelImage /// \param Number of components found in the image /// static void ConnectedComponentsImage(mitk::Image::Pointer & image, mitk::Image::Pointer& mask, mitk::Image::Pointer &outimage, unsigned int& num_components); /// /// \brief GrabLabel /// \param MultiLabelImage /// \param outimage /// \param label /// static void GrabLabel(mitk::Image::Pointer & image, mitk::Image::Pointer & outimage, unsigned int label); /// /// \brief itkInsertLabel /// \param image /// \param maskImage /// \param label /// static void InsertLabel(mitk::Image::Pointer & image, mitk::Image::Pointer & maskImage, unsigned int label); /// /// \brief ErodeGrayscale /// \param image /// \param outimage /// \param radius /// \param d /// static void ErodeGrayscale(mitk::Image::Pointer & image, unsigned int radius, mitk::CLUtil::MorphologicalDimensions d, mitk::Image::Pointer & outimage ); /// /// \brief DilateGrayscale /// \param image /// \param outimage /// \param radius /// \param d /// static void DilateGrayscale(mitk::Image::Pointer & image, unsigned int radius, mitk::CLUtil::MorphologicalDimensions d, mitk::Image::Pointer & outimage ); /// /// \brief FillHoleGrayscale /// \param image /// \param outimage /// static void FillHoleGrayscale(mitk::Image::Pointer & image, mitk::Image::Pointer & outimage); /// /// \brief ProbabilityMap /// \param sourceImage /// \param mean /// \param std_dev /// \param resultImage /// static void ProbabilityMap(const mitk::Image::Pointer& sourceImage, double mean, double std_dev, mitk::Image::Pointer& resultImage); template static void itkCountVoxel( TImageType * image, std::map & map) { auto it = itk::ImageRegionIterator< TImageType >(image,image->GetLargestPossibleRegion()); while(!it.IsAtEnd()) { if(map.find(it.Value()) == map.end()) map[it.Value()] = 0; map[it.Value()]++; ++it; } } template static void itkCountVoxel(TImageType* image, typename TImageType::PixelType label, unsigned int & count ) { itk::ImageRegionConstIterator inputIter(image, image->GetLargestPossibleRegion()); while(!inputIter.IsAtEnd()) { if(inputIter.Value() == label) ++count; ++inputIter; } } template static inline void itkCountVoxel(TImageType * mask, unsigned int & n_numSamples) { auto mit = itk::ImageRegionConstIterator(mask, mask->GetLargestPossibleRegion()); while (!mit.IsAtEnd()) { if(mit.Value() > 0) n_numSamples++; ++mit; } } template static void itkSampleLabel(TImageType1* image, TImageType2* output, double acceptrate, unsigned int label) { std::srand (time(nullptr)); itk::ImageRegionConstIterator< TImageType1 > inputIter(image, image->GetLargestPossibleRegion()); itk::ImageRegionIterator< TImageType2 > outputIter(output, output->GetLargestPossibleRegion()); while (!inputIter.IsAtEnd()) { double r = (double)(rand()) / RAND_MAX; if(inputIter.Get() == label && r < acceptrate) outputIter.Set(label); ++inputIter; ++outputIter; } } template static void itkSampleLabel(TImageType* image, mitk::Image::Pointer & output, unsigned int n_samples_drawn) { std::srand (time(nullptr)); typename TImageType::Pointer itk_out = TImageType::New(); itk_out->SetRegions(image->GetLargestPossibleRegion()); itk_out->SetDirection(image->GetDirection()); itk_out->SetOrigin(image->GetOrigin()); itk_out->SetSpacing(image->GetSpacing()); itk_out->Allocate(); itk_out->FillBuffer(0); itk::ImageRegionConstIterator< TImageType > inputIter(image, image->GetLargestPossibleRegion()); itk::ImageRegionIterator< TImageType > outputIter(itk_out, itk_out->GetLargestPossibleRegion()); for(unsigned int i = 0 ; i < n_samples_drawn ;) { double r = (double)(rand()) / RAND_MAX; if(inputIter.Value() != 0 && r < 0.01 && outputIter.Value() == 0) { outputIter.Set(inputIter.Value()); i++; } ++inputIter; ++outputIter; if(inputIter.IsAtEnd()) { inputIter.GoToBegin(); outputIter.GoToBegin(); } } mitk::CastToMitkImage(itk_out, output); } private: template static void itkErodeGrayscale(TImageType * image, mitk::Image::Pointer & outimage , unsigned int radius, mitk::CLUtil::MorphologicalDimensions d); template static void itkDilateGrayscale(TImageType * image, mitk::Image::Pointer & outimage , unsigned int radius, mitk::CLUtil::MorphologicalDimensions d); template static void itkFillHoleGrayscale(TImageType * image, mitk::Image::Pointer & outimage); template< typename TImageType > static void itkInsertLabel(TImageType * maskImage, mitk::Image::Pointer & outimage, unsigned int label) { typename TImageType::Pointer itk_out; if(outimage.IsNull()) // create if necessary { MITK_INFO << "Initialize new image"; itk_out = TImageType::New(); itk_out->SetSpacing(maskImage->GetSpacing()); itk_out->SetDirection(maskImage->GetDirection()); itk_out->SetOrigin(maskImage->GetOrigin()); itk_out->SetRegions(maskImage->GetLargestPossibleRegion()); itk_out->Allocate(); itk_out->FillBuffer(0); }else { mitk::CastToItkImage(outimage, itk_out); } itk::ImageRegionIterator oit(itk_out,itk_out->GetLargestPossibleRegion()); itk::ImageRegionConstIterator mit(maskImage,maskImage->GetLargestPossibleRegion()); while(!mit.IsAtEnd()) { if(mit.Value() != 0) { oit.Set(label); } ++oit; ++mit; } mitk::CastToMitkImage(itk_out,outimage); } template< typename TImageType > static void itkGrabLabel(TImageType * image, mitk::Image::Pointer & outimage, unsigned int label) { typedef itk::Image TOutType; TOutType::Pointer itk_out = TOutType::New(); itk_out->SetRegions(image->GetLargestPossibleRegion()); itk_out->SetDirection(image->GetDirection()); itk_out->SetOrigin(image->GetOrigin()); itk_out->SetSpacing(image->GetSpacing()); itk_out->Allocate(); itk::ImageRegionConstIterator iit(image, image->GetLargestPossibleRegion()); itk::ImageRegionIterator oit(itk_out,itk_out->GetLargestPossibleRegion()); while(!iit.IsAtEnd()) { if(iit.Value() == static_cast(label)) oit.Set(1); else oit.Set(0); ++iit; ++oit; } mitk::CastToMitkImage(itk_out, outimage); } template static void itkMergeLabels(TImagetype * img, const std::map & map) { auto it = itk::ImageRegionIterator(img,img->GetLargestPossibleRegion()); while(!it.IsAtEnd()) { if(map.find(it.Value())!=map.end()) it.Set( map.at(it.Value()) ); ++it; } } template static void itkConnectedComponentsImage(TImageType * image, mitk::Image::Pointer& mask, mitk::Image::Pointer &outimage, unsigned int& num_components) { typedef itk::Image MaskImageType; MaskImageType::Pointer itk_mask; if(mask.IsNull()) { itk_mask = MaskImageType::New(); itk_mask->SetRegions(image->GetLargestPossibleRegion()); itk_mask->SetDirection(image->GetDirection()); itk_mask->SetOrigin(image->GetOrigin()); itk_mask->SetSpacing(image->GetSpacing()); itk_mask->Allocate(); itk_mask->FillBuffer(1); }else{ mitk::CastToItkImage(mask,itk_mask); } typedef itk::ConnectedComponentImageFilter FilterType; typename FilterType::Pointer cc_filter = FilterType::New(); cc_filter->SetMaskImage(itk_mask.GetPointer()); cc_filter->SetInput(image); cc_filter->SetBackgroundValue(0); cc_filter->Update(); num_components = cc_filter->GetObjectCount(); mitk::CastToMitkImage(cc_filter->GetOutput(), outimage); } template< typename TImageType > static void itkCreateCheckerboardMask(TImageType * image, mitk::Image::Pointer & outimage); template< typename TImageType > static void itkInterpolateCheckerboardPrediction(TImageType * checkerboard_prediction, mitk::Image::Pointer & checkerboard_mask, mitk::Image::Pointer & outimage); template static void itkSumVoxelForLabel(TImageType* image, const mitk::Image::Pointer & source , typename TImageType::PixelType label, double & val ); template static void itkSqSumVoxelForLabel(TImageType* image, const mitk::Image::Pointer & source, typename TImageType::PixelType label, double & val ); template static void itkFitStructuringElement(TStructuringElement & se, MorphologicalDimensions d, int radius); template static void itkDilateBinary(TImageType * sourceImage, mitk::Image::Pointer& resultImage, int radius , MorphologicalDimensions d); template static void itkErodeBinary(TImageType * sourceImage, mitk::Image::Pointer& resultImage, int radius, MorphologicalDimensions d); template static void itkClosingBinary(TImageType * sourceImage, mitk::Image::Pointer& resultImage, int radius, MorphologicalDimensions d); template static void itkFillHolesBinary(itk::Image* sourceImage, mitk::Image::Pointer& resultImage); template static void itkLogicalAndImages(const TImageType * image1, const mitk::Image::Pointer & image2, mitk::Image::Pointer & outimage); template static void itkGaussianFilter(TImageType * image, mitk::Image::Pointer & smoothed ,double sigma); + template + static void itkDifferenceOfGaussianFilter(TImageType * image, mitk::Image::Pointer & smoothed, double sigma1, double sigma2); + template static void itkProbabilityMap(const TImageType * sourceImage, double mean, double std_dev, mitk::Image::Pointer& resultImage); + template + static void itkHessianOfGaussianFilter(itk::Image* itkImage, double variance, std::vector &out); + template + static void itkLaplacianOfGaussianFilter(itk::Image* itkImage, double variance, mitk::Image::Pointer &output); + template + static void itkLocalHistograms(itk::Image* itkImage, std::vector &out, int size, int bins); }; } //namespace MITK #endif diff --git a/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix.h b/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix.h index 3ff9f0e086..65793f1b18 100644 --- a/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix.h +++ b/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix.h @@ -1,47 +1,62 @@ #ifndef mitkGIFCooccurenceMatrix_h #define mitkGIFCooccurenceMatrix_h #include #include #include namespace mitk { class MITKCLUTILITIES_EXPORT GIFCooccurenceMatrix : public AbstractGlobalImageFeature { + /** + * \brief Calculates features based on the co-occurence matrix. + * + * This filter calculates features based on the Co-Occurence Matrix. + * + * \warning{ This is a legacy class only. If possible, avoid to use it. Use + * GIFCooccurenceMatrix2 instead.} + */ public: mitkClassMacro(GIFCooccurenceMatrix,AbstractGlobalImageFeature) itkFactorylessNewMacro(Self) itkCloneMacro(Self) GIFCooccurenceMatrix(); /** * \brief Calculates the Cooccurence-Matrix based features for this class. */ FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature) override; /** * \brief Returns a list of the names of all features that are calculated from this class */ FeatureNameListType GetFeatureNames() override; itkGetConstMacro(Range,double); itkSetMacro(Range, double); - itkGetConstMacro(Direction, unsigned int); - itkSetMacro(Direction, unsigned int); + + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + virtual void AddArguments(mitkCommandLineParser &parser); + struct GIFCooccurenceMatrixConfiguration { double range; unsigned int direction; + + double MinimumIntensity; + bool UseMinimumIntensity; + double MaximumIntensity; + bool UseMaximumIntensity; + int Bins; }; private: double m_Range; - unsigned int m_Direction; }; } #endif //mitkGIFCooccurenceMatrix_h diff --git a/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix2.h b/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix2.h new file mode 100644 index 0000000000..ad019eb78d --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix2.h @@ -0,0 +1,250 @@ +#ifndef mitkGIFCooccurenceMatrix2_h +#define mitkGIFCooccurenceMatrix2_h + +#include +#include +#include + +#include + +namespace mitk +{ + struct CoocurenceMatrixHolder + { + public: + CoocurenceMatrixHolder(double min, double max, int number); + + int IntensityToIndex(double intensity); + double IndexToMinIntensity(int index); + double IndexToMeanIntensity(int index); + double IndexToMaxIntensity(int index); + + double m_MinimumRange; + double m_MaximumRange; + double m_Stepsize; + int m_NumberOfBins; + Eigen::MatrixXd m_Matrix; + + }; + + struct CoocurenceMatrixFeatures + { + CoocurenceMatrixFeatures(): + JointMaximum(0), + JointAverage(0), + JointVariance(0), + JointEntropy(0), + RowMaximum(0), + RowAverage(0), + RowVariance(0), + RowEntropy(0), + FirstRowColumnEntropy(0), + SecondRowColumnEntropy(0), + DifferenceAverage(0), + DifferenceVariance(0), + DifferenceEntropy(0), + SumAverage(0), + SumVariance(0), + SumEntropy(0), + AngularSecondMoment(0), + Contrast(0), + Dissimilarity(0), + InverseDifference(0), + InverseDifferenceNormalised(0), + InverseDifferenceMoment(0), + InverseDifferenceMomentNormalised(0), + InverseVariance(0), + Correlation(0), + Autocorrelation(0), + ClusterTendency(0), + ClusterShade(0), + ClusterProminence(0), + FirstMeasureOfInformationCorrelation(0), + SecondMeasureOfInformationCorrelation(0) + { + } + + public: + double JointMaximum; + double JointAverage; + double JointVariance; + double JointEntropy; + double RowMaximum; + double RowAverage; + double RowVariance; + double RowEntropy; + double FirstRowColumnEntropy; + double SecondRowColumnEntropy; + double DifferenceAverage; + double DifferenceVariance; + double DifferenceEntropy; + double SumAverage; + double SumVariance; + double SumEntropy; + double AngularSecondMoment; + double Contrast; + double Dissimilarity; + double InverseDifference; + double InverseDifferenceNormalised; + double InverseDifferenceMoment; + double InverseDifferenceMomentNormalised; + double InverseVariance; + double Correlation; + double Autocorrelation; + double ClusterTendency; + double ClusterShade; + double ClusterProminence; + double FirstMeasureOfInformationCorrelation; + double SecondMeasureOfInformationCorrelation; + }; + + /** + * \brief Calculates features based on the co-occurence matrix. + * + * The co-occurence matrix describes the relations between voxels in a specific direction. The elements \f$m_{i,k} \f$ of the + * matrix count how often a voxel with the intensity \f$i \f$ has a neighbour in a certain direction with the intensity \f$ k \f$. + * The direction for each matrix is given by a directed vector \f$ \overrightarrow{d} \f$. + * + * It is important to calculate the matrices for all possible directions in order to obtain a rotation invariant feature. + * For the 3D case, this means that there are 26 possible directions. Using the symmetrical properties of the co-occurence + * matrix, it is then possible to calculate the features in all directions looking at 13 different directions. + * + * The standard length of the vector is 1, e.g. looking at direct neighbours. It is possible to look at more + * distance neighbours. This is achieved using the parameter range which defines the distance between + * two neighbouring voxels in number of voxels. The default value for this is 1. It can be changes using the Method + * SetRange() or by passing the option cooc2::range. + * + * There are two possible ways of combining the information obtained from the multiple directions. The first option + * is to calculate a common matrix for all directions and then use this matrix to calculate the describing features. + * The second method is to calculate a matrix for each direction, obtain the features and then report the mean and + * standard value of these features. Both mehtods are calcuated by this filters and reported, distinguisehd by either + * an "Overall" if a single matrix is used, a "Mean" for the mean Value, or an "Std.Dev." for the standard deviation. + * + * The connected areas are based on the binned image, the binning parameters can be set via the default + * parameters as described in AbstractGlobalImageFeature. The intensity used for the calculation is + * always equal to the bin number. It is also possible to determine the + * dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature. + * No other options are possible beside these two options. + * + * This feature calculator is activated by the option -cooccurence2 or -cooc2. + * + * The features are calculated based on a mask. It is assumed that the mask is + * of the type of an unsigned short image. All voxels with the value 1 are treated as masked. + * + * The following features are defined. We always give the notation for the overall matrix feature + * although those for the mean and std.dev. are basically equal. In the name, is replace + * by the distance of the neighbours. For the definitions of the feature, the probability of each + * intensity pair (i,k) \f$ p_{i,k} = \frac{m_{i,k}}{\sum_i \sum_k m_{i,k}} \f$. + * + * In addition, the marginal sum \f$ p_{i,\cdot} = p_{\cdot,k=i} = \sum_k p_{i,k} \f$, which is + * identical for both axis due to the symetrical nature of the matrix. Furthermore, the diagonal and + * cross diagnoal features are used: + * \f[ p_{i-k}(l) = \sum_i \sum_k p_{i,k} \delta(l - \| i -k \| ) \enspace \enspace l = 0, \dots, N_g -1 \f] + * \f[ p_{i+k}(l) = \sum_i \sum_k p_{i,k} \delta(l - ( i + k ) ) \enspace \enspace l = 2, \dots, 2 N_g \f] + * Here, \f$ \delta(x) \f$ is the dirac function, which is one for \f$x=0 \f$ and zero otherwise. + * - Co-occurenced Based Features ()::Overall Joint Maximum: + * \f[ \textup{Joint Maximum}= \textup{max}(p_{i,k}) \f] + * - Co-occurenced Based Features ()::Overall Joint Average: + * \f[ \textup{Joint Average} = \mu_{ja} = \sum_i \sum_k i p_{i,k} \f] + * - Co-occurenced Based Features ()::Overall Joint Variance: + * \f[ \textup{Joint Variance} = \sum_i \sum_k (i - \mu_{ja})^2 p_{i,k} \f] + * - Co-occurenced Based Features ()::Overall Joint Entropy: + * \f[ \textup{Joint Entropy} = e_j = - \sum_i \sum_k p_{i,k} \textup{log}_2 p_{i,k} \f] + * - Co-occurenced Based Features ()::Overall Row Maximum: + * \f[ \textup{Row Maximum}= \textup{max}(p_{i,\cdot}) \f] + * - Co-occurenced Based Features ()::Overall Row Average: + * \f[ \textup{Row Average} = \mu_{ra} = \sum_i i p_{i,\cdot} \f] + * - Co-occurenced Based Features ()::Overall Row Variance: + * \f[ \textup{Row Variance} = \sigma^2_{i, \cdot} = \sum_i (i - \mu_{ra})^2 p_{i,\cdot} \f] + * - Co-occurenced Based Features ()::Overall Row Entropy: + * \f[ \textup{Row Entropy} = e_r = - \sum_i p_{i,\cdot} \textup{log}_2 p_{i,\cdot} \f] + * - Co-occurenced Based Features ()::Overall First Row-Column Entropy: + * \f[ \textup{First Row-Column Entropy} = e_1 = - \sum_i \sum_k p_{i,k} \textup{log}_2 ( p_{i,\cdot} p_{\cdot,k}) \f] + * - Co-occurenced Based Features ()::Overall Second Row-Column Entropy: + * \f[ \textup{Second Row-Column Entropy} = e_2 = - \sum_i \sum_k p_{i,\cdot} p_{\cdot,k} \textup{log}_2 ( p_{i,\cdot} p_{\cdot,k}) \f] + * - Co-occurenced Based Features ()::Overall Difference Average: + * \f[ \textup{Difference Average} = \mu_{da} = \sum_l l p_{i-k}(l) \f] + * - Co-occurenced Based Features ()::Overall Difference Variance: + * \f[ \textup{Difference Variance} = \sum_l (i - \mu_{da})^2 p_{i-k}(l) \f] + * - Co-occurenced Based Features ()::Overall Difference Entropy: + * \f[ \textup{Difference Entropy} = - \sum_l p_{i-k}(l) \textup{log}_2 p_{i-k}(l) \f] + * - Co-occurenced Based Features ()::Overall Sum Average: + * \f[ \textup{Sum Average} = \mu_{sa} = \sum_l l p_{i+k}(l) \f] + * - Co-occurenced Based Features ()::Overall Sum Variance: + * \f[ \textup{Sum Variance} = \sum_l (i - \mu_{sa})^2 p_{i+k}(l) \f] + * - Co-occurenced Based Features ()::Overall Sum Entropy: + * \f[ \textup{Sum Entropy} = - \sum_l p_{i+k}(l) \textup{log}_2 p_{i+k}(l) \f] + * - Co-occurenced Based Features ()::Overall Angular Second Moment: + * \f[ \textup{Angular Second Moment} = \sum_i \sum_k p^2_{i,k} \f] + * - Co-occurenced Based Features ()::Overall Contrast: + * \f[ \textup{Contrast} = \sum_i \sum_k (i-k)^2 p_{i,k} \f] + * - Co-occurenced Based Features ()::Overall Dissimilarity: + * \f[ \textup{Dissimilarity} = \sum_i \sum_k \| i-k\| p^2_{i,k} \f] + * - Co-occurenced Based Features ()::Overall Inverse Difference: + * \f[ \textup{Inverse Difference} = \sum_i \sum_k \frac{p_{i,k}}{1+\| i-k\|} \f] + * - Co-occurenced Based Features ()::Overall Inverse Difference Normalized: + * \f[ \textup{Inverse Difference Normalized} = \sum_i \sum_k \frac{p_{i,k}}{1+\frac{\| i-k\|}{N_g}} \f] + * - Co-occurenced Based Features ()::Overall Inverse Difference Moment: + * \f[ \textup{Inverse Difference Moment} = \sum_i \sum_k \frac{p_{i,k}}{1+ ( i-k )^2} \f] + * - Co-occurenced Based Features ()::Overall Inverse Difference Moment Normalized: + * \f[ \textup{Inverse Difference Moment Normalized} = \sum_i \sum_k \frac{p_{i,k}}{1+\frac{( i-k ) ^2}{N_g}} \f] + * - Co-occurenced Based Features ()::Overall Inverse Variance: + * \f[ \textup{Inverse Difference Moment Normalized} = \sum_i \sum_k \frac{p_{i,k}}{(i-k)^2} \f] + * - Co-occurenced Based Features ()::Overall Correlation: + * \f[ \textup{Correlation} = \frac{1}{\sigma^2_{i,\cdot}} \sum_i \sum_k (i - \mu_{ra})(k - \mu_{ra}) p_{i,k} \f] + * - Co-occurenced Based Features ()::Overall Autocorrelation: + * \f[ \textup{Autocorrelation} = \sum_i \sum_k i k p_{i,k} \f] + * - Co-occurenced Based Features ()::Overall Cluster Tendency: + * \f[ \textup{Cluster Tendency} = \sum_i \sum_k (i + k - 2\mu_{ra})^2 p_{i,k} \f] + * - Co-occurenced Based Features ()::Overall Cluster Shade: + * \f[ \textup{Cluster Shade} = \sum_i \sum_k (i + k - 2\mu_{ra})^3 p_{i,k} \f] + * - Co-occurenced Based Features ()::Overall Cluster Prominence: + * \f[ \textup{Cluster Prominence} = \sum_i \sum_k (i + k - 2\mu_{ra})^4 p_{i,k} \f] + * - Co-occurenced Based Features ()::Overall First Measure of Information Correlation: + * \f[ \textup{First Measure of Information Correlation} = \frac{ e_j- e_1}{e_r} \f] + * - Co-occurenced Based Features ()::Overall Second Measure of Information Correlation: + * \f[ \textup{Second Measure of Information Correlation} = \sqrt{1- \exp(-2 (e_2 - e_j)} \f] + */ + class MITKCLUTILITIES_EXPORT GIFCooccurenceMatrix2 : public AbstractGlobalImageFeature + { + public: + mitkClassMacro(GIFCooccurenceMatrix2, AbstractGlobalImageFeature); + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + GIFCooccurenceMatrix2(); + + /** + * \brief Calculates the Cooccurence-Matrix based features for this class. + */ + virtual FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature) override; + + /** + * \brief Returns a list of the names of all features that are calculated from this class + */ + virtual FeatureNameListType GetFeatureNames() override; + + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + virtual void AddArguments(mitkCommandLineParser &parser); + virtual std::string GetCurrentFeatureEncoding() override; + + itkGetConstMacro(Range,double); + itkSetMacro(Range, double); + + struct GIFCooccurenceMatrix2Configuration + { + double range; + unsigned int direction; + + double MinimumIntensity; + double MaximumIntensity; + int Bins; + std::string prefix; + }; + + private: + double m_Range; + }; + +} +#endif //mitkGIFCooccurenceMatrix2_h diff --git a/Modules/Classification/CLUtilities/include/mitkGIFCurvatureStatistic.h b/Modules/Classification/CLUtilities/include/mitkGIFCurvatureStatistic.h new file mode 100644 index 0000000000..12cd15ce98 --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkGIFCurvatureStatistic.h @@ -0,0 +1,108 @@ +/*=================================================================== + +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 mitkGIFCurvatureStatistic_h +#define mitkGIFCurvatureStatistic_h + +#include +#include +#include + +namespace mitk +{ + + /** + * \brief Calculates features based on the co-occurence matrix. + * + * The Curvature is a measure for the bending of a surface and is therefore a measure for the description of the + * surface of an segmentation. + * + * THe curvature is calculated for each point of the surface of the given object and then a combined measure is + * produced. It measures the divergence of the orientation of an curve from the + * tangent of the curve. There are multiple ways to calculate the Curvature: + * + * Gaussian Curvature: The discrete gaussian curvature (K) is computed as \f$K(\textup{Corner Point v}) = 2 * \pi - \sum_{\textup{Neighoubring Voxel Surfaces f of v}} (\textup{Angle}_f \textup{at} v) \f$. + * Mean Curvature:The mean curvature (H) is computed as \f$H(\textup{Corner Point v}) = \textup{average over edges e neighbouring v of H(e)} \f$. + * with \f$H(edge e) = length(e)*dihedral_angle(e)\f$ + * Maximum (\f$k_max\f$) and Minimum (\f$k_min\f$) Principal Curvatures + * \f$k_max = H + sqrt(H^2 - K)\f$ + * \f$k_min = H - sqrt(H^2 - K)\f$ + * Excepting spherical and planar surfaces which have equal principal curvatures, + * the curvature at a point on a surface varies with the direction one "sets off" + * from the point. For all directions, the curvature will pass through two extrema: + * a minimum (\f$k_min\f$) and a maximum (\f$k_max\f$) which occur at mutually orthogonal + * directions to each other. + * + * This method does not take any parameters. + * + * This feature calculator is activated by the option -curvature or -cur. + * + * The features are calculated based on a mask, which is converted into a mesh. + * + * The following features are defined. All features are calculated for all four possible + * curvation calculation methods (Gaussian, Mean, Minimum, Maximum). The principal way + * of calculating these features is the same, the used curvation is indicated by in the + * feature name: + * + * - Curvature Feature::Minimum Curvature: + * The minimum curvature for the whole given mask + * - Curvature Feature::Maximum Curvature: + * The maximum curvature for the whole given mask + * - Curvature Feature::Mean Curvature: + * The mean curvature for the whole given mask + * - Curvature Feature::Standard Deviation Curvature: + * The standard deviation curvature for the whole given mask + * - Curvature Feature::Skewness Curvature: + * The skewness curvature for the whole given mask + * - Curvature Feature::Mean Positive Curvature: + * The mean curvature of all positive curvatures from the whole given mask + * - Curvature Feature::Standard Deviation Positive Curvature: + * The Standard Deviation curvature of all positive curvatures from the whole given mask + * - Curvature Feature::Skewness Positive Curvature: + * The Skewness curvature of all positive curvatures from the whole given mask + * - Curvature Feature::Mean Negative Curvature: + * The mean curvature of all Negative curvatures from the whole given mask + * - Curvature Feature::Standard Deviation Negative Curvature: + * The Standard Deviation curvature of all Negative curvatures from the whole given mask + * - Curvature Feature::Skewness Negative Curvature: + * The Skewness curvature of all Negative curvatures from the whole given mask + */ + class MITKCLUTILITIES_EXPORT GIFCurvatureStatistic : public AbstractGlobalImageFeature + { + public: + mitkClassMacro(GIFCurvatureStatistic,AbstractGlobalImageFeature) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + GIFCurvatureStatistic(); + + /** + * \brief Calculates the Cooccurence-Matrix based features for this class. + */ + virtual FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature); + + /** + * \brief Returns a list of the names of all features that are calculated from this class + */ + virtual FeatureNameListType GetFeatureNames(); + + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + virtual void AddArguments(mitkCommandLineParser &parser); + + private: + }; +} +#endif //mitkGIFCurvatureStatistic_h diff --git a/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderHistogramStatistics.h b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderHistogramStatistics.h new file mode 100644 index 0000000000..20274b1a12 --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderHistogramStatistics.h @@ -0,0 +1,139 @@ +/*=================================================================== + +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 mitkGIFFirstOrderHistogramStatistics_h +#define mitkGIFFirstOrderHistogramStatistics_h + +#include +#include +#include + +namespace mitk +{ + /** + * \brief Calulates first order features based on a histogram. + * + * This class can be used to calculate first order features based on a histogram. + * For each feature, two variations are given, once the value of the feature that is + * obtained if the mean intensity of the histogram bins is used and the + * histogram bin that corresponds to the feature value. See AbstractGlobalImageFeature for more + * information on the histogram initialization. The histogram gives a probability \f$p_i\f$ for the + * intensity \f$x_i\f$ that is linked to the bin \f$i\f$. The histogram bins start at index 1. + * + * This feature calculator is activated by the option "-first-order-histogram" or "-foh". + * Beside the options for the histogram definitions, which are given in the description of AbstractGlobalImageFeature , no + * additional parameters are available. + * + * The features are calculated based on a mask. It is assumed that the mask is + * of the type of an unsigned short image and all voxels with an value of 1 + * are treated as masked. + * + * The resulting features are: + * - First Order Histogram::Mean Value: The mean intensity of all voxels, calulated by \f$ \mu_x = \sum p_i x_i\f$. + * - First Order Histogram::Variance Value The variance intensity is calculated as : \f$ \sigma^2 = \sum p_i (x_i - \mu_x)^2\f$. + * - First Order Histogram::Skewness Value: \f[ skewness = \frac{\sum p_i (x_i - \mu_x)^3}{\sigma^3} \f] + * - First Order Histogram::Excess Kurtosis Value: \f[ skewness = \frac{\sum p_i (x_i - \mu_x)^4}{\sigma^4} - 3 \f] + * - First Order Histogram::Median Value: The median intensity value based on the histogram values. + * - First Order Histogram::Minimum Value: The minimum observed intensity value. + * - First Order Histogram::Percentile 10 Value: The intensity that is equal or greater than 10% of all observed intensities. + * - First Order Histogram::Percentile 90 Value: The intensity that is equal or greater than 90% of all observed intensities. + * - First Order Histogram::Maximum Value: The maximum observerd intensity value. + * - First Order Histogram::Mode Value: The most common intensity value, i.e. the value of the bin with the highest probability. + * - First Order Histogram::Interquantile Range Value: The intensity difference between Percentile 75% (\f$ P75\f$) and Percentile 25% (\f$ P25\f$). + * - First Order Histogram::Range Value: The difference between the observed maximum and minimum intensity. + * - First Order Histogram::Mean Absolute Deviation Value: \f[ \textup{mean absolute deviation} = \sum p_i \left \| (x_i - \mu_x) \right \| \f] + * - First Order Histogram::Robust Mean Value: The mean of all intensities between the 10% and 90% quantile. + * - First Order Histogram::Robust Mean Absolute Deviation Value: The Mean absolute deviation for all values between the 10% and 90% quantile. It is based on the robust mean value. + * - First Order Histogram::Median Absolute Deviation Value: \f[ \textup{mean absolute deviation} = \sum p_i \left \| (x_i - \textup{median}) \right \| \f] + * - First Order Histogram::Coefficient of Variation Value: \f[ \frac{\sigma_x}{\mu_x} \f] + * - First Order Histogram::Quantile coefficient of Dispersion Value: \f[ \textup{Quantile coefficient of Dispersion} = \frac{P75 - P25}{P75 + P25} \f] + * - First Order Histogram::Entropy Value: The entropy is only based on histogram bins with a probability greater than 0.0000001: \f[ \textup{entropy} = - \sum p_i \textup{log}_2 p_i \f] + * - First Order Histogram::Uniformity Value: \f$ \sum p_i^2 \f$ + * - First Order Histogram::Mean Index: The mean index of all voxels, calulated by \f$ \mu_i = \sum p_i i\f$. + * - First Order Histogram::Variance Index: The variance index is calculated as : \f$ \sigma_i^2 = \sum p_i (i - \mu_i)^2\f$. + * - First Order Histogram::Skewness Index: \f[ skewness = \frac{\sum p_i (i - \mu_i)^3}{\sigma_i^3} \f] + * - First Order Histogram::Excess Kurtosis Index: \f[ skewness = \frac{\sum p_i (i - \mu_i)^4}{\sigma_i^4} - 3 \f] + * - First Order Histogram::Median Index: The median index value based on the histogram values. + * - First Order Histogram::Minimum Index: The index of the minimum observed intensity value. + * - First Order Histogram::Percentile 10 Index: The index oft the intensity that is equal or greater than 10% of all observed intensities. + * - First Order Histogram::Percentile 90 Index: The index of the intensity that is equal or greater than 90% of all observed intensities. + * - First Order Histogram::Maximum Index: The index of the maximum observerd intensity value. + * - First Order Histogram::Mode Index: The index of the most common intensity value, i.e. the index of the bin with the highest probability. + * - First Order Histogram::Interquantile Range Index: The index difference between Percentile 75% (\f$ P75\f$) and Percentile 25% (\f$ P25\f$). + * - First Order Histogram::Range Index: The index difference between the index of the observed maximum and minimum intensity. + * - First Order Histogram::Mean Absolute Deviation Index: \f[ \textup{mean absolute deviation} = \sum p_i \left \| (i - \mu_i) \right \| \f] + * - First Order Histogram::Robust Mean Absolute Deviation Index: The Mean absolute deviation for all values between the 10% and 90% quantile. It is based on the robust mean value. + * - First Order Histogram::Median Absolute Deviation Index: \f[ \textup{mean absolute deviation} = \sum p_i \left \| (i - \textup{median}) \right \| \f] + * - First Order Histogram::Coefficient of Variation Index: \f[ \frac{\sigma_i}{\mu_i} \f] + * - First Order Histogram::Quantile coefficient of Dispersion Index: \f[ \textup{Quantile coefficient of Dispersion} = \frac{P75 - P25}{P75 + P25} \f] + * - First Order Histogram::Entropy Index: The entropy is only based on histogram bins with a probability greater than 0.0000001: \f$ \textup{entropy} = - \sum p_i \textup{log}_2 p_i \f$. Note that this is the same as the entropy value. + * - First Order Histogram::Uniformity Index: \f$ \sum p_i^2 \f$. Note that this is the same as the uniformity value. + * - First Order Histogram::Maximum Gradient: The maximum difference between the probability of three neighbouring bins. For bins at the edge of the histogram, only two bins are used for the calulation. + * - First Order Histogram::Maximum Gradient Index: The index of the bin that belongs to the maximum gradient. + * - First Order Histogram::Minimum Gradient: The minimum difference between the probability of three neighbouring bins. For bins at the edge of the histogram, only two bins are used for the calulation. + * - First Order Histogram::Minimum Gradient Index:The index of the bin that belongs to the minimum gradient. + * - First Order Histogram::Robust Mean Index: The mean index of all intensities between the 10% and 90% quantile. + * - First Order Histogram::Number of Bins: The number of bins in the histogram. This is rather for control, as this parameter is likely to be determined by the configuration rather than the image. + * - First Order Histogram::Bin Size: The binsize of the bins from the histogram. This is rather for control, as this parameter is likely to be determined by the configuration rather than the image. + */ + class MITKCLUTILITIES_EXPORT GIFFirstOrderHistogramStatistics : public AbstractGlobalImageFeature + { + public: + mitkClassMacro(GIFFirstOrderHistogramStatistics,AbstractGlobalImageFeature) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + GIFFirstOrderHistogramStatistics(); + + /** + * \brief Calculates the Cooccurence-Matrix based features for this class. + */ + virtual FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature) override; + + /** + * \brief Returns a list of the names of all features that are calculated from this class + */ + virtual FeatureNameListType GetFeatureNames() override; + + itkGetConstMacro(Range,double); + itkSetMacro(Range, double); + itkGetConstMacro(HistogramSize,int); + itkSetMacro(HistogramSize, int); + itkGetConstMacro(UseCtRange,bool); + itkSetMacro(UseCtRange, bool); + itkGetConstMacro(BinSize, double); + itkSetMacro(BinSize, double); + + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + virtual void AddArguments(mitkCommandLineParser &parser); + virtual std::string GetCurrentFeatureEncoding() override; + + + struct ParameterStruct { + double MinimumIntensity; + double MaximumIntensity; + int Bins; + std::string prefix; + }; + + private: + double m_Range; + int m_HistogramSize; + bool m_UseCtRange; + double m_BinSize; + }; +} +#endif //mitkGIFFirstOrderHistogramStatistics_h diff --git a/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderStatistics.h b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderStatistics.h index b72957af00..12f6bcd227 100644 --- a/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderStatistics.h +++ b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderStatistics.h @@ -1,63 +1,164 @@ /*=================================================================== 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 mitkGIFFirstOrderStatistics_h #define mitkGIFFirstOrderStatistics_h #include #include #include namespace mitk { class MITKCLUTILITIES_EXPORT GIFFirstOrderStatistics : public AbstractGlobalImageFeature { public: + /** + * \brief Calculates first order statistics of the given image. + * + * The first order statistics for the intensity distribution within a given Region of Interest (ROI) + * is caluclated. The ROI is defined using a mask. + * + * The features are calculated on a quantified image. If the bin-size is too big, the obtained values + * can be errornous and missleading. It is therefore important to use enough bins. The binned approach is + * used in order to avoid floating-point related errors. + * + * This feature calculator is activated by the option -first-order or -fo. + * + * The connected areas are based on the binned image, the binning parameters can be set via the default + * parameters as described in AbstractGlobalImageFeature. It is also possible to determine the + * dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature. + * No other options are possible beside these two options. + * + * The features are calculated based on a mask. It is assumed that the mask is + * of the type of an unsigned short image. All voxels with the value 1 are treated as masked. + * + * The following features are then defined using the (binned) voxel intensity \f$ x_i \f$ of each voxel, the probability + * an intensity \f$ p_x \f$, and the overall number of voxels within the mask \f$ N_v \f$: + * - First Order::Mean: The mean intensity within the ROI + * \f[ \textup{Mean}= \mu = \frac{1}{N_v} \sum x_i \f] + * - First Order::Unbiased Variance: An unbiased estimation of the variance: + * \f[ \textup{Unbiased Variance} = \frac{1}{N_v - 1} \sum \left( x_i - \mu \right)^2 \f] + * - First Order::Biased Variance: An biased estimation of the variance. If not specified otherwise, this is + * used as the variance: + * \f[ \textup{Biased Variance} = \sigma^2 = \frac{1}{N_v} \sum \left( x_i - \mu \right)^2 \f] + * - First Order::Unbiased Standard Deviation: Estimation of diversity within the intensity values + * \f[ \textup{Unbiased Standard Deviation} = \sqrt{\frac{1}{N_v-1} \sum \left( x_i - \mu \right)^2} \f] + * - First Order::Biased Standard Deviation: Estimation of diversity within the intensity values + * \f[ \textup{Biased Standard Deviation} = \sigma = \sqrt{\frac{1}{N_v} \sum \left( x_i - \mu \right)^2} \f] + * - First Order::Skewness: + * \f[ \textup{Skewness} = \frac{\frac{1}{N_v} \sum \left( x_i - \mu \right)^3}{\sigma^3} \f] + * - First Order::Kurtosis: The kurtosis is a measurement of the peakness of the given + * distirbution: + * \f[ \textup{Kurtosis} = \frac{\frac{1}{N_v} \sum \left( x_i - \mu \right)^4}{\sigma^4} \f] + * - First Order::Excess Kurtosis: The kurtosis is a measurement of the peakness of the given + * distirbution. The excess kurtosis is similar to the kurtosis, but is corrected by a fisher correction, + * ensuring that a gaussian distribution has an excess kurtosis of 0. + * \f[ \textup{Excess Kurtosis} = \frac{\frac{1}{N_v} \sum \left( x_i - \mu \right)^4}{\sigma^4} - 3 \f] + * - First Order::Median: The median is defined as the median of the all intensities in the ROI. + * - First Order::Minimum: The minimum is defined as the minimum of the all intensities in the ROI. + * - First Order::05th Percentile: \f$ P_{5\%} \f$ The 5% percentile. 5% of all voxel do have this or a lower intensity. + * - First Order::10th Percentile: \f$ P_{10\%} \f$ The 10% percentile. 10% of all voxel do have this or a lower intensity. + * - First Order::15th Percentile: \f$ P_{15\%} \f$ The 15% percentile. 15% of all voxel do have this or a lower intensity. + * - First Order::20th Percentile: \f$ P_{20\%} \f$ The 20% percentile. 20% of all voxel do have this or a lower intensity. + * - First Order::25th Percentile: \f$ P_{25\%} \f$ The 25% percentile. 25% of all voxel do have this or a lower intensity. + * - First Order::30th Percentile: \f$ P_{30\%} \f$ The 30% percentile. 30% of all voxel do have this or a lower intensity. + * - First Order::35th Percentile: \f$ P_{35\%} \f$ The 35% percentile. 35% of all voxel do have this or a lower intensity. + * - First Order::40th Percentile: \f$ P_{40\%} \f$ The 40% percentile. 40% of all voxel do have this or a lower intensity. + * - First Order::45th Percentile: \f$ P_{45\%} \f$ The 45% percentile. 45% of all voxel do have this or a lower intensity. + * - First Order::50th Percentile: \f$ P_{50\%} \f$ The 50% percentile. 50% of all voxel do have this or a lower intensity. + * - First Order::55th Percentile: \f$ P_{55\%} \f$ The 55% percentile. 55% of all voxel do have this or a lower intensity. + * - First Order::60th Percentile: \f$ P_{60\%} \f$ The 60% percentile. 60% of all voxel do have this or a lower intensity. + * - First Order::65th Percentile: \f$ P_{65\%} \f$ The 65% percentile. 65% of all voxel do have this or a lower intensity. + * - First Order::70th Percentile: \f$ P_{70\%} \f$ The 70% percentile. 70% of all voxel do have this or a lower intensity. + * - First Order::75th Percentile: \f$ P_{75\%} \f$ The 75% percentile. 75% of all voxel do have this or a lower intensity. + * - First Order::80th Percentile: \f$ P_{80\%} \f$ The 80% percentile. 80% of all voxel do have this or a lower intensity. + * - First Order::85th Percentile: \f$ P_{85\%} \f$ The 85% percentile. 85% of all voxel do have this or a lower intensity. + * - First Order::90th Percentile: \f$ P_{90\%} \f$ The 90% percentile. 90% of all voxel do have this or a lower intensity. + * - First Order::95th Percentile: \f$ P_{95\%} \f$ The 95% percentile. 95% of all voxel do have this or a lower intensity. + * - First Order::Maximum: The maximum is defined as the minimum of the all intensities in the ROI. + * - First Order::Range: The range of intensity values is defined as the difference between the maximum + * and minimum intensity in the ROI. + * - First Order::Interquartile Range: The difference between the 75% and 25% quantile. + * - First Order::Mean Absolute Deviation: The mean absolute deviation gives the mean distance of each + * voxel intensity to the overal mean intensity and is a measure of the dispersion of the intensity form the + * mean value: + * \f[ \textup{Mean Absolute Deviation} = \frac{1}{N_v} \sum \left \| x_i - \mu \right \| \f] + * - First Order::Robust Mean: The mean intensity within the ROI for all voxels between the 10% and 90% quantile: + * \f[ \textup{Robust Mean}= \mu_R = \frac{1}{N_{vr}} \sum x_i \f] + * - First Order::Robust Mean Absolute Deviation: The absolute deviation of all intensities within the ROI for + * all voxels between the 10% and 90% quantilefrom the robust mean intensity: + * \f[ \textup{Robust Mean Absolute Deviation}= \mu_R = \frac{1}{N_{vr}} \sum \left \| x_i - \mu_R \right \| \f] + * - First Order::Median Absolute Deviation: Similar to the mean absolute deviation, but uses the median + * instead of the mean to measure the center of the distribution. + * - First Order::Coefficient Of Variation: Measures the dispersion of the intensity distribution: + * \f[ \textup{Coefficient Of Variation} = \frac{sigma}{\mu} \f] + * - First Order::Quantile Coefficient Of Dispersion: A robust alternative to teh coefficient of variance: + * \f[ \textup{Quantile Coefficient Of Dispersion} = \frac{P_{75\%} - P_{25\%} }{P_{75\%} + P_{25\%}} \f] + * - First Order::Energy: The intensity energy: + * \f[ \textup{Energy} = \sum x_i ^2 \f] + * - First Order::Root Mean Square: Root mean square is an important measure for the error. + * \f[ \textup{Root Mean Square} = \sqrt{\frac{\sum x_i ^2}{N_v}} \f] + * - First Order::Uniformity: + * \f[ \textup{Uniformity} = \sum p_x^2 \f] + * - First Order::Entropy: + * \f[ \textup{Entropy} = - \sum p_x \textup{log}_2(p_x) \f] + * - First Order::Entropy: + * \f[ \textup{Entropy} = - \sum p_x \textup{log}_2(p_x) \f] + * - First Order::Covered Image Intensity Range: Percentage of the image intensity range (maximum - minimum in whole + * image) that is covered by the ROI. + * - First Order::Sum: The sum of all intensities. It is correlated to the mean intensity. + * \f[ \textup{Sum} = \sum x_i \f] + * - First Order::Mode: The most common intensity. + * - First Order::Mode Probability: The likelihood of the most common intensity. + * - First Order::Number Of Voxels: \f$ N_v \f$ the number of voxels covered by the ROI. + * - First Order::Image Dimension: The dimensionality of the image (e.g. 2D, 3D, etc.). + * - First Order::Number Of Voxels: The product of all spacing along all dimensions. In 3D, this is equal to the + * volume. + * - First Order::Number Of Voxels: The volume of a single voxel. If the dimensionality is only 2D, this is the + * surface of an voxel. + */ mitkClassMacro(GIFFirstOrderStatistics,AbstractGlobalImageFeature) itkFactorylessNewMacro(Self) itkCloneMacro(Self) GIFFirstOrderStatistics(); /** - * \brief Calculates the Cooccurence-Matrix based features for this class. + * \brief Calculates the First Order Features based on a binned version of the image. */ FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature) override; /** * \brief Returns a list of the names of all features that are calculated from this class */ FeatureNameListType GetFeatureNames() override; + virtual std::string GetCurrentFeatureEncoding() override; + + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + virtual void AddArguments(mitkCommandLineParser &parser); - itkGetConstMacro(Range,double); - itkSetMacro(Range, double); - itkGetConstMacro(HistogramSize,int); - itkSetMacro(HistogramSize, int); - itkGetConstMacro(UseCtRange,bool); - itkSetMacro(UseCtRange, bool); struct ParameterStruct { - int m_HistogramSize; - bool m_UseCtRange; + double MinimumIntensity; + double MaximumIntensity; + int Bins; + std::string prefix; }; - private: - double m_Range; - int m_HistogramSize; - bool m_UseCtRange; }; } #endif //mitkGIFFirstOrderStatistics_h diff --git a/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelDistanceZone.h b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelDistanceZone.h new file mode 100644 index 0000000000..870e556978 --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelDistanceZone.h @@ -0,0 +1,178 @@ +#ifndef mitkGIFGreyLevelDistanceZone_h +#define mitkGIFGreyLevelDistanceZone_h + +#include +#include +#include + +#include + +namespace mitk +{ + struct GreyLevelDistanceZoneFeatures + { + GreyLevelDistanceZoneFeatures() : + SmallDistanceEmphasis(0), + LargeDistanceEmphasis(0), + LowGreyLevelEmphasis(0), + HighGreyLevelEmphasis(0), + SmallDistanceLowGreyLevelEmphasis(0), + SmallDistanceHighGreyLevelEmphasis(0), + LargeDistanceLowGreyLevelEmphasis(0), + LargeDistanceHighGreyLevelEmphasis(0), + GreyLevelNonUniformity(0), + GreyLevelNonUniformityNormalized(0), + ZoneDistanceNonUniformity(0), + ZoneDistanceNoneUniformityNormalized(0), + ZonePercentage(0), + GreyLevelMean(0), + GreyLevelVariance(0), + ZoneDistanceMean(0), + ZoneDistanceVariance(0), + ZoneDistanceEntropy(0) + { + } + + public: + double SmallDistanceEmphasis; + double LargeDistanceEmphasis; + double LowGreyLevelEmphasis; + double HighGreyLevelEmphasis; + double SmallDistanceLowGreyLevelEmphasis; + double SmallDistanceHighGreyLevelEmphasis; + double LargeDistanceLowGreyLevelEmphasis; + double LargeDistanceHighGreyLevelEmphasis; + double GreyLevelNonUniformity; + double GreyLevelNonUniformityNormalized; + double ZoneDistanceNonUniformity; + double ZoneDistanceNoneUniformityNormalized; + double ZonePercentage; + double GreyLevelMean; + double GreyLevelVariance; + double ZoneDistanceMean; + double ZoneDistanceVariance; + double ZoneDistanceEntropy; + }; + + + class MITKCLUTILITIES_EXPORT GIFGreyLevelDistanceZone : public AbstractGlobalImageFeature + { + /** + * \brief Calculates the Grey Level Distance Zone + * + * This class can be used to calculate Grey Level Distance Zone as presented in Thibault et al. 2014. + * + * The basic idea behind the Grey Level Distance Zone based features is to count the connected areas + * with a given intensity value \f$x_i\f$ and a given distance to the border of each segmentation \f$d_i\f$. + * Several features are then calculated based on a matrix, that gives the number of occurence for each + * combination of \f$x_i\f$ and \f$ d_i \f$ as \f$m_{x,d}\f$. + * + * This feature calculator is activated by the option -grey-level-distance-zone or -gldz. + * + * The connected areas are based on the binned image, the binning parameters can be set via the default + * parameters as described in AbstractGlobalImageFeature. It is also possible to determine the + * dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature. + * No other options are possible beside these two options. + * + * The features are calculated based on a mask. It is assumed that the mask is + * of the type of an unsigned short image. It is expected that the image contains only voxels with value 0 and 1, + * of which all voxels with an value equal to one are treated as masked. + * + * The features depend on the distance to the border of the segmentation ROI. In some cases, the border + * definition might be different from the definition of the masked area, for example, if small openings + * in the mask should not influence the distance. Due to that, it is possible to submit a second mask, + * named Morphological Mask to the features that is then used to calculate the distance of each voxel to + * border of the segmented area. The morpological mask can be either set by the function call SetMorphMask() + * or by the corresponding global option. (Not parsed by the filter itself, but by the command line tool). + * + * Beside the complete matrix, which is represented by its individual elements \f$m_{x,d}\f$, som eadditional + * values are used for the definition. \f$N_g\f$ is the number of discrete grey levels, \f$ N_d\f$ the number + * (or maximum value) of possible distances, and \f$N_s\f$ the total number of zones. + * \f$m_{x,d}\f$ gives the number of connected areas with the discrete + * grey level x and distance to the boarder of d. Corresponding, \f$p_{x,d} = \frac{m_{x,d}}{N_s} gives the relativ + * probability of this matrix cell. \f$ \f$N_v\f$ is the number of voxels. In addition, the marginal + * sums \f$m_{x,\cdot} = m_x = \sum_d m_{x,d} \f$ , representing the sum of all zones with a given intensity, and + * sums \f$m_{\cdot, d} = m_d = \sum_x m_{x,d} \f$ , representing the sum of all zones with a given distance, are used. + * The distance are given as the number of voxels to the border and the voxel intensity is given as the + * bin number of the binned image, starting with 1. + * + * The following features are then defined: + * - Grey Level Distance Zone::Small Distance Emphasis: + * \f[ \textup{Small Distance Emphasis}= \frac{1}{N_s} \sum_d \frac{m_d}{d^2} \f] + * - Grey Level Distance Zone::Large Distance Emphasis: + * \f[ \textup{Large Distance Emphasis}= \frac{1}{N_s} \sum_d d^2 m_d \f] + * - Grey Level Distance Zone::Low Grey Level Emphasis: + * \f[ \textup{Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \frac{m_x}{x^2} \f] + * - Grey Level Distance Zone::High Grey Level Emphasis: + * \f[ \textup{High Grey Level Emphasis}= \frac{1}{N_s} \sum_x x^2 m_x \f] + * - Grey Level Distance Zone::Small Distance Low Grey Level Emphasis: + * \f[ \textup{Small Distance Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d \frac{ m_{x,d}}{x^2 d^2}\f] + * - Grey Level Distance Zone::Small Distance High Grey Level Emphasis: + * \f[ \textup{Small Distance High Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d \frac{x^2 m_{x,d}}{d^2}\f] + * - Grey Level Distance Zone::Large Distance Low Grey Level Emphasis: + * \f[ \textup{Large Distance Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d \frac{d^2 m_{x,d}}{x^2}\f] + * - Grey Level Distance Zone::Large Distance High Grey Level Emphasis: + * \f[ \textup{Large Distance High Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d \x^2 d^2 m_{x,d} \f] + * - Grey Level Distance Zone::Grey Level Non-Uniformity: + * \f[ \textup{Grey Level Non-Uniformity}= \frac{1}{N_s} \sum_x m_x^2 \f] + * - Grey Level Distance Zone::Grey Level Non-Uniformity Normalized: + * \f[ \textup{Grey Level Non-Uniformity Normalized}= \frac{1}{N_s^2} \sum_x m_x^2 \f] + * - Grey Level Distance Zone::Zone Distance Non-Uniformity: + * \f[ \textup{Grey Level Non-Uniformity}= \frac{1}{N_s} \sum_d m_d^2 \f] + * - Grey Level Distance Zone::Zone Distance Non-Uniformity Normalized: + * \f[ \textup{Grey Level Non-Uniformity Normalized}= \frac{1}{N_s^2} \sum_d m_d^2 \f] + * - Grey Level Distance Zone::Zone Percentage: The ratio of zones to the possible zones: + * \f[ \textup{Zone Percentage}= \frac{N_s}{N_v} \f] + * - Grey Level Distance Zone::Grey Level Mean: + * \f[ \textup{Grey Level Mean}= \mu_x = \sum_x \sum_d x p_{x,d} \f] + * - Grey Level Distance Zone::Grey Level Variance: + * \f[ \textup{Grey Level Variance} = \sum_x \sum_d \left(x - \mu_x \right)^2 p_{x,d} \f] + * - Grey Level Distance Zone::Zone Distance Mean: + * \f[ \textup{Zone Distance Mean}= \mu_d = \sum_x \sum_d d p_{x,d} \f] + * - Grey Level Distance Zone::Zone Distance Variance: + * \f[ \textup{Zone Distance Variance} = \sum_x \sum_d \left(d - \mu_d \right)^2 p_{x,d} \f] + * - Grey Level Distance Zone::Zone Distance Entropy : + * \f[ \textup{Zone Distance Entropy} = - \sum_x \sum_d p_{x,d} \textup{log}_2 ( p_{x,d} ) \f] + * - Grey Level Distance Zone::Grey Level Entropy : + * \f[ \textup{Grey Level Entropy} = - \sum_x \sum_d p_{x,d} \textup{log}_2 ( p_{x,d} ) \f] + */ + public: + mitkClassMacro(GIFGreyLevelDistanceZone, AbstractGlobalImageFeature); + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + GIFGreyLevelDistanceZone(); + + /** + * \brief Calculates the Cooccurence-Matrix based features for this class. + */ + virtual FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature) override; + + /** + * \brief Returns a list of the names of all features that are calculated from this class + */ + virtual FeatureNameListType GetFeatureNames() override; + + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + virtual void AddArguments(mitkCommandLineParser &parser); + + virtual std::string GetCurrentFeatureEncoding() override; + + struct GIFGreyLevelDistanceZoneConfiguration + { + mitk::Image::Pointer distanceMask; + + mitk::IntensityQuantifier::Pointer Quantifier; + + unsigned int direction; + double MinimumIntensity; + double MaximumIntensity; + int Bins; + std::string prefix; + }; + + private: + }; + +} +#endif //mitkGIFGreyLevelDistanceZone_h diff --git a/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelRunLength.h b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelRunLength.h new file mode 100644 index 0000000000..8e7298afff --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelRunLength.h @@ -0,0 +1,119 @@ +#ifndef mitkGIFGreyLevelRunLength_h +#define mitkGIFGreyLevelRunLength_h + +#include +#include +#include + +namespace mitk +{ + /** + * \brief Calculates the Run Length based features. + * + * Grey Level Run Length based features are calcualted using the Run-Length-Matrix and were originally + * defined by Galloway (1975). The basic idea behind this feature is to measure the length of + * lines with similar intensity in an image. This allows to asses line-based structures in an image. + * For this, the Run-Length-Matrix created that gives the number of lines \f$ m_{x,l} \f$ with the intensity \f$ x \f$ and + * the length of \f$ l \f$ with the given intensity. + * + * The image is quantified prior to the calculation of the features. This reduces the number of + * available intensity values. Instead of using the pure intensity value, the features are + * calculated using the number of the bins as intensity value \f$ x_i \f$. The parameter of the + * quantification of the image can be controlled using the general binning parameters as defined + * in AbstractGlobalImageFeature. + * + * By default, the calculation is based on a 26 neighourhood for 3D and a 8 neighbourhood in 2D. It is further + * possible to exclude directions from the calculation, e.g. calculating the feature in 2D, even if a + * 3D image is passed. This is controlled by determine the + * dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature. + * No other options are possible beside these two options. + * + * This feature calculator is activated by the option -run-length or -rl. + * + * The features are calculated based on a mask. It is assumed that the mask is + * of the same type as the input image. All voxels with a value greater 0.5 are treated as masked. + * + * Several values are definied for the definition of the features. \f$ N_v \f$ is the number of masked voxels, + * \f$N_s \f$ is the number of different lines, \f$ m_{x,\cdot} = \sum_l m{x,l} \f$ is the number of all lines + * with a given intensity value, and likewise \f$ m_{\cdot, l} = \sum_x m{x,l} \f$ is the number of all lines + * with a given length. There are two options how to make this feature orientation-invariant. Either calculating a + * single matrix for all directions and then extracting the features or by calculating a matrix per direction, + * calculating all features and then reporting the mean and standard deviation for each feature. The result + * of the first option is given with the extension "Comb.", the results for the second with "Std." and "Means". + * All three options are always calculated, although we state only one in the following list: + * - Run Length::Short run emphasis Comb.: + * \f[ \textup{Short run emphasis}= \frac{1}{N_s} \sum_l { \frac{m_{\cdot, l}}{l^2} } \f] + * - Run Length::Long run emphasis Comb.: + * \f[ \textup{Long run emphasis}= \frac{1}{N_s} \sum_l { m_{\cdot, l} l^2} \f] + * - Run Length::Low grey level run emphasis Comb.: + * \f[ \textup{Low grey level run emphasis}= \frac{1}{N_s} \sum_x { \frac{m_{x,\cdot}}{x^2} } \f] + * - Run Length::High grey level run emphasis Comb.: + * \f[ \textup{High grey level run emphasis}= \frac{1}{N_s} \sum_x { m_{x,\cdot} x^2} \f] + * - Run Length::Short run low grey level emphasis Comb.: + * \f[ \textup{Short run low grey level emphasis}= \frac{1}{N_s} \sum_x \sum_l { \frac{m_{x,l}}{x^2 l^2} } \f] + * - Run Length::Short run high grey level emphasis Comb.: + * \f[ \textup{Short run high grey level emphasis}= \frac{1}{N_s} \sum_x \sum_l { \frac{x^2 m_{x,s}}{l^2} } \f] + * - Run Length::Long run low grey level emphasis Comb.: + * \f[ \textup{Long run low grey level emphasis}= \frac{1}{N_s} \sum_x \sum_l { \frac{l^2 m_{x,l}}{x^2} } \f] + * - Run Length::Long run high grey level emphasis Comb.: + * \f[ \textup{Long run high grey level emphasis}= \frac{1}{N_s} \sum_x \sum_l { x^2 l^2 m_{x,l} } \f] + * - Run Length::Grey level nonuniformity Comb.: + * \f[ \textup{Grey level nonuniformity}= \frac{1}{N_s} \sum_x m_{x,\cdot}^2 \f] + * - Run Length::Grey level nonuniformity normalized Comb.: + * \f[ \textup{Grey level nonuniformity normalized}= \frac{1}{N_s^2} \sum_x m_{x,\cdot}^2 \f] + * - Run Length::Run length nonuniformity: + * \f[ \textup{Run length nonuniformity}= \frac{1}{N_s} \sum_l m_{\cdot, l}^2 \f] + * - Run Length::Run length nonuniformity normalized: + * \f[ \textup{Run length nonuniformity normalized}= \frac{1}{N_s^2} \sum_l m_{\cdot, l}^2 \f] + * - Run Length::Run percentage: The ratio of realized runs to the theoretical limit of zones: + * \f[ \textup{Run percentage}= \frac{N_s}{N_v} \f] + * - Run Length::Grey level variance: + * \f[ \textup{Grey level variance} = \frac{1}{N_s} \sum_x (x -mu_x)^2 m_{x, \cdot} \f] + * - Run Length::Run length variance: + * \f[ \textup{Run length variance} = \frac{1}{N_s} \sum_l (l -mu_l)^2 m_{\cdot, l} \f] + * - Run Length::Run length entropy: This feature would be equivalent with + * the Grey Level Entropy, which is therefore not included. It is based on the likelihood + * for a given intensity- size combination \f$ p_{x,s} = \frac{m_{x,s}}{N_s} \f$. : + * \f[ \textup{Run length entropy} = \sum_x \sum_s p_{x,s} \textup{log}_2 \left( p{_x,s} \right) \f] + * - Run Length::Number of runs: The overall number of realized runs: + * \f[ \textup{Number of runs} = \sum m_{x, l} \f] + */ + class MITKCLUTILITIES_EXPORT GIFGreyLevelRunLength : public AbstractGlobalImageFeature + { + public: + mitkClassMacro(GIFGreyLevelRunLength,AbstractGlobalImageFeature) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + GIFGreyLevelRunLength(); + + /** + * \brief Calculates the Cooccurence-Matrix based features for this class. + */ + virtual FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature) override; + + /** + * \brief Returns a list of the names of all features that are calculated from this class + */ + virtual FeatureNameListType GetFeatureNames() override; + + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + virtual void AddArguments(mitkCommandLineParser &parser); + + virtual std::string GetCurrentFeatureEncoding() override; + + struct ParameterStruct + { + unsigned int m_Direction; + + double MinimumIntensity; + double MaximumIntensity; + int Bins; + std::string featurePrefix; + }; + + private: + + }; +} +#endif //mitkGIFGreyLevelRunLength_h diff --git a/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelSizeZone.h b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelSizeZone.h new file mode 100644 index 0000000000..1072abe0ac --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelSizeZone.h @@ -0,0 +1,180 @@ +#ifndef mitkGIFGreyLevelSizeZone_h +#define mitkGIFGreyLevelSizeZone_h + +#include +#include +#include + +#include + +namespace mitk +{ + struct GreyLevelSizeZoneMatrixHolder + { + public: + GreyLevelSizeZoneMatrixHolder(double min, double max, int number, int maxSize); + + int IntensityToIndex(double intensity); + double IndexToMinIntensity(int index); + double IndexToMeanIntensity(int index); + double IndexToMaxIntensity(int index); + + double m_MinimumRange; + double m_MaximumRange; + double m_Stepsize; + int m_NumberOfBins; + int m_MaximumSize; + Eigen::MatrixXd m_Matrix; + + }; + + struct GreyLevelSizeZoneFeatures + { + GreyLevelSizeZoneFeatures() : + SmallZoneEmphasis(0), + LargeZoneEmphasis(0), + LowGreyLevelEmphasis(0), + HighGreyLevelEmphasis(0), + SmallZoneLowGreyLevelEmphasis(0), + SmallZoneHighGreyLevelEmphasis(0), + LargeZoneLowGreyLevelEmphasis(0), + LargeZoneHighGreyLevelEmphasis(0), + GreyLevelNonUniformity(0), + GreyLevelNonUniformityNormalized(0), + ZoneSizeNonUniformity(0), + ZoneSizeNoneUniformityNormalized(0), + ZonePercentage(0), + GreyLevelMean(0), + GreyLevelVariance(0), + ZoneSizeMean(0), + ZoneSizeVariance(0), + ZoneSizeEntropy(0) + { + } + + public: + double SmallZoneEmphasis; + double LargeZoneEmphasis; + double LowGreyLevelEmphasis; + double HighGreyLevelEmphasis; + double SmallZoneLowGreyLevelEmphasis; + double SmallZoneHighGreyLevelEmphasis; + double LargeZoneLowGreyLevelEmphasis; + double LargeZoneHighGreyLevelEmphasis; + double GreyLevelNonUniformity; + double GreyLevelNonUniformityNormalized; + double ZoneSizeNonUniformity; + double ZoneSizeNoneUniformityNormalized; + double ZonePercentage; + double GreyLevelMean; + double GreyLevelVariance; + double ZoneSizeMean; + double ZoneSizeVariance; + double ZoneSizeEntropy; + }; + + + class MITKCLUTILITIES_EXPORT GIFGreyLevelSizeZone : public AbstractGlobalImageFeature + { + /** + * \brief Calculates the Grey level size zone based features. + * + * Grey level size zone based features are similar to Grey Level Cooccurence features. But instead + * of measuring the similarity within a given neighbourhood, the size of areas with the same intensity + * is assessed. For this, a matrix is created that gives the number of areas \f$ m_{x,s} \f$ with the intensity \f$ x \f$ and + * the size of \f$ s \f$. Each area is specified as connected voxels with the given intensity. + * + * The image is quantified prior to the calculation of the features. This reduces the number of + * available intensity values. Instead of using the pure intensity value, the features are + * calculated using the number of the bins as intensity value \f$ x_i \f$. The parameter of the + * quantification of the image can be controlled using the general binning parameters as defined + * in AbstractGlobalImageFeature. + * + * By default, the calculation is based on a 26 neighourhood for 3D and a 8 neighbourhood in 2D. It is further + * possible to exclude directions from the calculation, e.g. calculating the feature in 2D, even if a + * 3D image is passed. This is controlled by determine the + * dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature. + * No other options are possible beside these two options. + * + * This feature calculator is activated by the option -grey-level-sizezone or -glsz. + * + * The features are calculated based on a mask. It is assumed that the mask is + * of the type of an unsigned short image. All voxels with the value 1 are treated as masked. + * + * Several values are definied for the definition of the features. \f$ N_v \f$ is the number of masked voxels, + * \f$N_s \f$ is the number of different zones, \f$ m_{x,\cdot} = \sum_s m{x,s} \f$ is the number of all areas + * with a given intensity value, and likewise \f$ m_{\cdot, s} = \sum_x m{x,s} \f$ is the number of all areas + * with a given size. The features are then defined as: + * - Grey Level Size Zone::Small Zone Emphasis: + * \f[ \textup{Small Zone Emphasis}= \frac{1}{N_s} \sum_s { \frac{m_{\cdot, s}}{s^2} } \f] + * - Grey Level Size Zone::Large Zone Emphasis: + * \f[ \textup{Large Zone Emphasis}= \frac{1}{N_s} \sum_s { m_{\cdot, s} s^2} \f] + * - Grey Level Size Zone::Low Grey Level Zone Emphasis: + * \f[ \textup{Low Grey Level Zone Emphasis}= \frac{1}{N_s} \sum_x { \frac{m_{x,\cdot}}{x^2} } \f] + * - Grey Level Size Zone::High Grey Level Zone Emphasis: + * \f[ \textup{High Grey Level Zone Emphasis}= \frac{1}{N_s} \sum_x { m_{x,\cdot} x^2} \f] + * - Grey Level Size Zone::Small Zone Low Grey Level Emphasis: + * \f[ \textup{Small Zone Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_s { \frac{m_{x,s}}{x^2 s^2} } \f] + * - Grey Level Size Zone::Small Zone High Grey Level Emphasis: + * \f[ \textup{Small Zone High Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_s { \frac{x^2 m_{x,s}}{s^2} } \f] + * - Grey Level Size Zone::Large Zone Low Grey Level Emphasis: + * \f[ \textup{Large Zone Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_s { \frac{s^2 m_{x,s}}{x^2} } \f] + * - Grey Level Size Zone::Large Zone High Grey Level Emphasis: + * \f[ \textup{Large Zone High Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_s { x^2 s^2 m_{x,s} } \f] + * - Grey Level Size Zone::Grey Level Non-Uniformity: + * \f[ \textup{Grey Level Non-Uniformity}= \frac{1}{N_s} \sum_x m_{x,\cdot}^2 \f] + * - Grey Level Size Zone::Grey Level Non-Uniformity Normalized: + * \f[ \textup{Grey Level Non-Uniformity Normalized}= \frac{1}{N_s^2} \sum_x m_{x,\cdot}^2 \f] + * - Grey Level Size Zone::Zone Size Non-Uniformity: + * \f[ \textup{Zone Size Non-Uniformity}= \frac{1}{N_s} \sum_s m_{\cdot, s}^2 \f] + * - Grey Level Size Zone::Zone Size Non-Uniformity Normalized: + * \f[ \textup{Zone Size Non-Uniformity Normalized}= \frac{1}{N_s^2} \sum_s m_{\cdot, s}^2 \f] + * - Grey Level Size Zone::Zone Percentage: The ratio of realized areas to the theoretical limit of zones: + * \f[ \textup{Zone Percentage}= \frac{N_s}{N_v} \f] + * - Grey Level Size Zone::Grey Level Mean: + * \f[ \textup{Grey Level Mean} = \mu_x = \frac{1}{N_s} \sum_x x m_{x, \cdot} \f] + * - Grey Level Size Zone::Grey Level Variance: + * \f[ \textup{Grey Level Variance} = \frac{1}{N_s} \sum_x (x -mu_x)^2 m_{x, \cdot} \f] + * - Grey Level Size Zone::Zone Size Mean: + * \f[ \textup{Zone Size Mean} = \mu_s = \frac{1}{N_s} \sum_s s m_{\cdot, s} \f] + * - Grey Level Size Zone::Grey Level Variance: + * \f[ \textup{Grey Level Variance} = \frac{1}{N_s} \sum_s (s -mu_s)^2 m_{\cdot, s} \f] + * - Grey Level Size Zone::Zone Size Entropy: This feature would be equivalent with + * the Grey Level Entropy, which is therefore not included. It is based on the likelihood + * for a given intensity- size combination \f$ p_{x,s} = \frac{m_{x,s}}{N_s} \f$. : + * \f[ \textup{Zone Size Entropy} = \sum_x \sum_s p_{x,s} \textup{log}_2 \left( p{_x,s} \right) \f] + */ + public: + mitkClassMacro(GIFGreyLevelSizeZone, AbstractGlobalImageFeature); + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + GIFGreyLevelSizeZone(); + + /** + * \brief Calculates the Cooccurence-Matrix based features for this class. + */ + virtual FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature) override; + + /** + * \brief Returns a list of the names of all features that are calculated from this class + */ + virtual FeatureNameListType GetFeatureNames() override; + + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + virtual void AddArguments(mitkCommandLineParser &parser); + virtual std::string GetCurrentFeatureEncoding() override; + + struct GIFGreyLevelSizeZoneConfiguration + { + unsigned int direction; + + double MinimumIntensity; + double MaximumIntensity; + int Bins; + std::string prefix; + }; + }; + +} +#endif //mitkGIFGreyLevelSizeZone_h diff --git a/Modules/Classification/CLUtilities/include/mitkGIFImageDescriptionFeatures.h b/Modules/Classification/CLUtilities/include/mitkGIFImageDescriptionFeatures.h new file mode 100644 index 0000000000..1057a80e29 --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkGIFImageDescriptionFeatures.h @@ -0,0 +1,91 @@ +/*=================================================================== + +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 mitkGIFImageDescriptionFeatures_h +#define mitkGIFImageDescriptionFeatures_h + +#include +#include +#include + +namespace mitk +{ + /** + * \brief Calculates simple features that describe the given image / mask + * + * This class can be used to calculated simple image describing features that + * can be used to compare different images. + * + * This feature calculator is activated by the option "-image-diagnostic" or "-id". + * There are no parameters to further define the behavior of this feature calculator. + * + * The paramters for this image are calculated from two images, a mask and an intenstiy + * image. It is assumed that the mask is + * of the type of an unsigned short image and all voxels with an value larger or equal + * to one are treated as masked. (Standard MITK mask) + * + * The definition of X, Y, and Z axis depends on the image format and does not necessarily + * correlates with axial, coronal, and sagittal. + * + * The features of this calculator are: + * - Diagnostic::Dimension X: The number of voxels in the intensity image along the first image dimension. + * - Diagnostic::Image Dimension Y: The number of voxels in the intensity image along the second image dimension. + * - Diagnostic::Image Dimension Z: The number of voxels in the intensity image along the third image dimension. + * - Diagnostic::Image Spacing X: The spacing of the intensity image in the first image dimension. + * - Diagnostic::Image Spacing Y: The spacing of the intensity image in the second image dimension. + * - Diagnostic::Image Spacing Z: The spacing of the intensity image in the third image dimension. + * - Diagnostic::Image Mean intensity: The mean intensity of the whole image. + * - Diagnostic::Image Minimum intensity: The minimum intensity that occurs in the whole image. + * - Diagnostic::Image Maximum intensity: The maximum intensity that occurs in the whole image. + * - Diagnostic::Mask Dimension X: The number of voxels in the mask image along the first image dimension. + * - Diagnostic::Mask Dimension Y: The number of voxels in the mask image along the second image dimension. + * - Diagnostic::Mask Dimension Z: The number of voxels in the mask image along the third image dimension. + * - Diagnostic::Mask bounding box X: The distance between the maximum and minimum position of a masked voxel along the first axis. + * - Diagnostic::Mask bounding box X: The distance between the maximum and minimum position of a masked voxel along the second axis. + * - Diagnostic::Mask bounding box X: The distance between the maximum and minimum position of a masked voxel along the third axis. + * - Diagnostic::Mask Spacing X: The spacing of the mask image in the first image dimension. + * - Diagnostic::Mask Spacing Y: The spacing of the mask image in the second image dimension. + * - Diagnostic::Mask Spacing Z: The spacing of the mask image in the third image dimension. + * - Diagnostic::Mask Voxel count: The number of voxels that are masked. + * - Diagnostic::Mask Mean intensity: The mean intensity of all voxel that are masked. Also depends on the intensity image. + * - Diagnostic::Mask Minimum intensity The minimum masked intensity in the intensity image. + * - Diagnostic::Mask Maximum intensity The maximum masked intensity in the intensity image. + */ + class MITKCLUTILITIES_EXPORT GIFImageDescriptionFeatures : public AbstractGlobalImageFeature + { + public: + mitkClassMacro(GIFImageDescriptionFeatures,AbstractGlobalImageFeature) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + GIFImageDescriptionFeatures(); + + /** + * \brief Calculates the Cooccurence-Matrix based features for this class. + */ + virtual FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature) override; + + /** + * \brief Returns a list of the names of all features that are calculated from this class + */ + virtual FeatureNameListType GetFeatureNames() override; + + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + virtual void AddArguments(mitkCommandLineParser &parser); + + }; +} +#endif //mitkGIFImageDescriptionFeatures_h diff --git a/Modules/Classification/CLUtilities/include/mitkGIFIntensityVolumeHistogramFeatures.h b/Modules/Classification/CLUtilities/include/mitkGIFIntensityVolumeHistogramFeatures.h new file mode 100644 index 0000000000..e1e30f5edc --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkGIFIntensityVolumeHistogramFeatures.h @@ -0,0 +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 mitkGIFIntensityVolumeHistogramFeatures_h +#define mitkGIFIntensityVolumeHistogramFeatures_h + +#include +#include +#include + +namespace mitk +{ + /** + * \brief Calculates the Intensity Volume Histogram features + * + * This class can be used to calculate the volume histogram and features that are calculated from + * it. It is based on the intensity-volume histogram (IVH) which describes the relation between the + * grey level index i (and the corresponding intensity \f§x_i\f$) and the volume fraction \f$f\f$ that + * with an intensity that is equal or greater than \f$x_i\f$. This feature is original proposed in + * El Naqa et al. Exploring feature-based approaches in PET images for prediciting cancer treatment outcomes. + * Pattern recognition 2009. + * + * This feature calculator is activated by the option "-intensity-volume-histogram" or "-ivoh". + * Beside the configuration of the histogram, which follows the describtion given in AbstractGlobalImageFeature + * there are no additional parameters to configure this feature. Remark that, different from other features, + * the number of bins is 1000 by default and not 256. + * + * The features are calculated based on a mask. It is assumed that the mask is + * of the type of an unsigned short image and all voxels with an value of greater than zero + * are treated as masked. + * + * The resulting features are: + * - Intensity Volume Histogram::Volume fration at 0.10 intensity: The volume fraction with an intensity + * of greater or equal to 10% of the maximum intensity. + * - Intensity Volume Histogram::Volume fration at 0.90 intensity: The volume fraction with an intensity + * of greater or equal to 90% of the maximum intensity. + * - Intensity Volume Histogram::Intensity at 0.10 volume: The highest intensity so that at least + * 10% of the masked image area has the same or higher intensity. + * - Intensity Volume Histogram::Intensity at 0.90 volume: The highest intensity so that at least + * 10% of the masked image area has the same or higher intensity. + * - Intensity Volume Histogram::Difference volume fration at 0.10 and 0.90 intensity: The difference + * between the volume fraction at 10% intensity and 90% intensity. + * - Intensity Volume Histogram::Difference intensity at 0.10 and 0.90 volume: The intensity difference + * between the intenstiy of 90% of the volume and 10% volume. + * - Intensity Volume Histogram::Area under IVH curve: If the IVH is interpreted as curve, this value represents + * the area under the curve. It is calculated using the bin indexes rather than the intensity values. + */ + class MITKCLUTILITIES_EXPORT GIFIntensityVolumeHistogramFeatures : public AbstractGlobalImageFeature + { + public: + mitkClassMacro(GIFIntensityVolumeHistogramFeatures, AbstractGlobalImageFeature); + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + GIFIntensityVolumeHistogramFeatures(); + + /** + * \brief Calculates the Cooccurence-Matrix based features for this class. + */ + virtual FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature); + + /** + * \brief Returns a list of the names of all features that are calculated from this class + */ + virtual FeatureNameListType GetFeatureNames(); + virtual void AddArguments(mitkCommandLineParser &parser); + + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + + virtual std::string GetCurrentFeatureEncoding() override; + + private: + }; +} +#endif //mitkGIFIntensityVolumeHistogramFeatures_h diff --git a/Modules/Classification/CLUtilities/include/mitkGIFLocalIntensity.h b/Modules/Classification/CLUtilities/include/mitkGIFLocalIntensity.h new file mode 100644 index 0000000000..3e2344ff12 --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkGIFLocalIntensity.h @@ -0,0 +1,82 @@ +/*=================================================================== + +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 mitkGIFLocalIntensity_h +#define mitkGIFLocalIntensity_h + +#include +#include +#include + +namespace mitk +{ + /** + * \brief Calculates the local intensity features + * + * This class can be used to calcualte the local intensity. The local intensity is defined as the + * mean intensity in a cube with the volume \f$ 1 \textup{cm}^3\f$ which correspond to a radius + * of approximate 0.62 cm. + * + * This feature calculator is activated by the option -local-intensity or -loci. + * + * The range that is used to calculate the local intensity can be set using the function SetRange. + * To set it with parameters set the option loci::range which expects an float value that is + * interpreted as mm length of the radius. The default value is 6.2. + * + * The features are calculated based on a mask. It is assumed that the mask is + * of the type of an unsigned short image and all voxels with an value of greater than zero + * are treated as masked. + * + * The resulting features are: + * - Local Intensity::Local Intensity Peak: This value is defined as the local intensity of the + * voxel with the highest intensity. If there are multiple voxels with the highest intensity, the mean + * of all local intensites of these voxels are reported. + * - Local Intensity::Global Intensity Peak: This value is defined as the highest local intensity + * that occur for all voxels within the mask. + */ + class MITKCLUTILITIES_EXPORT GIFLocalIntensity : public AbstractGlobalImageFeature + { + public: + mitkClassMacro(GIFLocalIntensity, AbstractGlobalImageFeature); + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + GIFLocalIntensity(); + + /** + * \brief Calculates the Cooccurence-Matrix based features for this class. + */ + virtual FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature); + + /** + * \brief Returns a list of the names of all features that are calculated from this class + */ + virtual FeatureNameListType GetFeatureNames(); + + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + virtual void AddArguments(mitkCommandLineParser &parser); + + virtual std::string GetCurrentFeatureEncoding() override; + + itkGetConstMacro(Range, double); + itkSetMacro(Range, double); + + private: + + double m_Range; + }; +} +#endif //mitkGIFLocalIntensity_h diff --git a/Modules/Classification/CLUtilities/include/mitkGIFGrayLevelRunLength.h b/Modules/Classification/CLUtilities/include/mitkGIFNeighbourhoodGreyLevelDifference.h similarity index 58% rename from Modules/Classification/CLUtilities/include/mitkGIFGrayLevelRunLength.h rename to Modules/Classification/CLUtilities/include/mitkGIFNeighbourhoodGreyLevelDifference.h index 27a1c0f631..66cbc509a0 100644 --- a/Modules/Classification/CLUtilities/include/mitkGIFGrayLevelRunLength.h +++ b/Modules/Classification/CLUtilities/include/mitkGIFNeighbourhoodGreyLevelDifference.h @@ -1,51 +1,51 @@ -#ifndef mitkGIFGrayLevelRunLength_h -#define mitkGIFGrayLevelRunLength_h +#ifndef mitkGIFNeighbourhoodGreyLevelDifference_h +#define mitkGIFNeighbourhoodGreyLevelDifference_h #include #include #include namespace mitk { - class MITKCLUTILITIES_EXPORT GIFGrayLevelRunLength : public AbstractGlobalImageFeature + class MITKCLUTILITIES_EXPORT GIFNeighbourhoodGreyLevelDifference : public AbstractGlobalImageFeature { public: - mitkClassMacro(GIFGrayLevelRunLength,AbstractGlobalImageFeature) + mitkClassMacro(GIFNeighbourhoodGreyLevelDifference,AbstractGlobalImageFeature) itkFactorylessNewMacro(Self) itkCloneMacro(Self) - GIFGrayLevelRunLength(); + GIFNeighbourhoodGreyLevelDifference(); /** * \brief Calculates the Cooccurence-Matrix based features for this class. */ FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature) override; /** * \brief Returns a list of the names of all features that are calculated from this class */ FeatureNameListType GetFeatureNames() override; itkGetConstMacro(Range,double); itkSetMacro(Range, double); itkGetConstMacro(UseCtRange, bool); itkSetMacro(UseCtRange, bool); + + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + virtual void AddArguments(mitkCommandLineParser &parser); - itkGetConstMacro(Direction, unsigned int); - itkSetMacro(Direction, unsigned int); struct ParameterStruct { bool m_UseCtRange; double m_Range; unsigned int m_Direction; }; private: double m_Range; bool m_UseCtRange; - unsigned int m_Direction; }; } -#endif //mitkGIFGrayLevelRunLength_h +#endif //mitkGIFNeighbourhoodGreyLevelDifference_h diff --git a/Modules/Classification/CLUtilities/include/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.h b/Modules/Classification/CLUtilities/include/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.h new file mode 100644 index 0000000000..6ebf4da4fd --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.h @@ -0,0 +1,99 @@ +/*=================================================================== + +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 mitkGIFNeighbourhoodGreyToneDifferenceFeatures_h +#define mitkGIFNeighbourhoodGreyToneDifferenceFeatures_h + +#include +#include +#include + +namespace mitk +{ + /** + * \brief Calculates the Neighbourhood Grey Tone Difference Features. + * + * This class can be used to calculate Neighbourhood Grey Tone Difference Features which have been introduced in + * Amadasun and King: Textural features corresponding to textural properties. IEEE Transactions on Systems, Man and Cybernetricy, 1989. + * + * The Neighbourhood Grey Tone Difference (NGTD) is based on a table and is calcualted using + * a defined neighbourhood for each voxel. Within this neighbourhood, the mean intensity of the neighbouring + * voxel is calculated \f$A_i\f$, i.e. the mean intensity of the neighbourhood excluding the center voxel. + * Based on this a table with four columns is calculated. The first column represents the voxel + * value, or in our implementation, the histogram bin index \f$i\f$. The second column represents the + * number of voxel with this intensity value \f$n_i\f$. The proability for each intensity value \f$p_i\f$, which + * is equal to the bin probability. And the sum of the absolut differences of the intensity of all voxels with this + * intensity and the mean intensity of their neighbourhood: \f[s_i = \sum \left \| i- A_i \right \| \f]. + * Additional \f$ N_v\f$ is the number of voxels, \f$ N_g \f$ the number of bins, and \f$N_{g,p}\f$ the number of + * bins with a non-zero probability. + * + * This feature calculator is activated by the option -neighbourhood-grey-tone-difference or -ngtd. + * + * The range that is used to calculate the local intensity can be set using the function SetRange. + * To set it with parameters set the option ngtd::range which expects an int value n that is + * interpreted as voxel count. The neighbourhood includes symetrical n voxels additional + * to the center voxel in each directions. The default value for this parameter is 1. + * + * The features are calculated based on a mask. It is assumed that the mask is + * of the type of an unsigned short image and all voxels with an value of greater than zero + * are treated as masked. + * + * For the definition of the features we use the sum, this is always the sum over the bins of the histogram. + * If the denominator of a feature evaluates to zero, the feature is defined as zero. + * The resulting features are: + * - Neighbourhood Grey Tone Difference::Coarsness: The coarsness is defined as : + * \f[ \textup{Coarsness}= \frac{1}{\sum p_i s_i} \f] + * - Neighbourhood Grey Tone Difference::Contrast: The contrast is defined as : + * \f[ \textup{contrast}= \left( \frac{1}{N_{g,p} ( N_{g,p} - 1) } \sum_i \sum_j p_i p_j (i-j)^2 \right) \left( \frac{1}{N_v} \sum s_i \right) \f] + * - Neighbourhood Grey Tone Difference::Busyness: for all bins with a non-zero probability + * \f[ \textup{busyness} = \frac{\sum p_i s_i}{\sum_i \sum_j \left \| i p_i - j p_j \right \| } \f] + * - Neighbourhood Grey Tone Difference::Complexity: for all bins with a non-zero probability + * \f[ \textup{complexity} = \frac{1}{N_v} \sum_i \sum_j \left \| i - j \right \| \frac{p_i s_i + p_j s_j}{p_i + p_j} \f] + * - Neighbourhood Grey Tone Difference::Strength: for all bins with a non-zero probability + * \f[ \textup{strength} = \frac{\sum_i \sum_j (p_i + p_j) (i + j)^2}{\sum_i s_i } \f] + */ + class MITKCLUTILITIES_EXPORT GIFNeighbourhoodGreyToneDifferenceFeatures : public AbstractGlobalImageFeature + { + public: + mitkClassMacro(GIFNeighbourhoodGreyToneDifferenceFeatures, AbstractGlobalImageFeature); + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + GIFNeighbourhoodGreyToneDifferenceFeatures(); + + /** + * \brief Calculates the Cooccurence-Matrix based features for this class. + */ + virtual FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature); + + /** + * \brief Returns a list of the names of all features that are calculated from this class + */ + virtual FeatureNameListType GetFeatureNames(); + + virtual std::string GetCurrentFeatureEncoding() override; + + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + virtual void AddArguments(mitkCommandLineParser &parser); + + itkSetMacro(Range, int); + itkGetConstMacro(Range, int); + + private: + int m_Range; + }; +} +#endif //mitkGIFNeighbourhoodGreyToneDifferenceFeatures_h diff --git a/Modules/Classification/CLUtilities/include/mitkGIFNeighbouringGreyLevelDependenceFeatures.h b/Modules/Classification/CLUtilities/include/mitkGIFNeighbouringGreyLevelDependenceFeatures.h new file mode 100644 index 0000000000..d915514c8f --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkGIFNeighbouringGreyLevelDependenceFeatures.h @@ -0,0 +1,237 @@ +#ifndef mitkGIFNeighbouringGreyLevelDependenceFeatures_h +#define mitkGIFNeighbouringGreyLevelDependenceFeatures_h + +#include +#include +#include + +#include + +namespace mitk +{ + struct NGLDMMatrixHolder + { + public: + NGLDMMatrixHolder(double min, double max, int number, int depenence); + + int IntensityToIndex(double intensity); + double IndexToMinIntensity(int index); + double IndexToMeanIntensity(int index); + double IndexToMaxIntensity(int index); + + double m_MinimumRange; + double m_MaximumRange; + double m_Stepsize; + int m_NumberOfDependences; + int m_NumberOfBins; + Eigen::MatrixXd m_Matrix; + + int m_NeighbourhoodSize; + unsigned long m_NumberOfNeighbourVoxels; + unsigned long m_NumberOfDependenceNeighbourVoxels; + unsigned long m_NumberOfNeighbourhoods; + unsigned long m_NumberOfCompleteNeighbourhoods; + }; + + struct NGLDMMatrixFeatures + { + NGLDMMatrixFeatures() : + LowDependenceEmphasis(0), + HighDependenceEmphasis(0), + LowGreyLevelCountEmphasis(0), + HighGreyLevelCountEmphasis(0), + LowDependenceLowGreyLevelEmphasis(0), + LowDependenceHighGreyLevelEmphasis(0), + HighDependenceLowGreyLevelEmphasis(0), + HighDependenceHighGreyLevelEmphasis(0), + GreyLevelNonUniformity(0), + GreyLevelNonUniformityNormalised(0), + DependenceCountNonUniformity(0), + DependenceCountNonUniformityNormalised(0), + DependenceCountPercentage(0), + GreyLevelVariance(0), + DependenceCountVariance(0), + DependenceCountEntropy(0), + DependenceCountEnergy(0), + MeanGreyLevelCount(0), + MeanDependenceCount(0), + ExpectedNeighbourhoodSize(0), + AverageNeighbourhoodSize(0), + AverageIncompleteNeighbourhoodSize(0), + PercentageOfCompleteNeighbourhoods(0), + PercentageOfDependenceNeighbours(0) + { + } + + public: + double LowDependenceEmphasis; + double HighDependenceEmphasis; + double LowGreyLevelCountEmphasis; + double HighGreyLevelCountEmphasis; + double LowDependenceLowGreyLevelEmphasis; + double LowDependenceHighGreyLevelEmphasis; + double HighDependenceLowGreyLevelEmphasis; + double HighDependenceHighGreyLevelEmphasis; + + double GreyLevelNonUniformity; + double GreyLevelNonUniformityNormalised; + double DependenceCountNonUniformity; + double DependenceCountNonUniformityNormalised; + + double DependenceCountPercentage; + double GreyLevelVariance; + double DependenceCountVariance; + double DependenceCountEntropy; + double DependenceCountEnergy; + double MeanGreyLevelCount; + double MeanDependenceCount; + + double ExpectedNeighbourhoodSize; + double AverageNeighbourhoodSize; + double AverageIncompleteNeighbourhoodSize; + double PercentageOfCompleteNeighbourhoods; + double PercentageOfDependenceNeighbours; + + }; + + /** + * \brief Calculates the Neighbouring Grey Level Dependence Features + * + * The Neighbouring Grey Level Dependence Features were proposed by Sun and Wee (1983) and + * capture the coarsness of the image texture. They are rotational invariant. + * + * The features are calculated on a matrix \f$ m \f$. To obtain the matrix, a neighbourhood + * around each feature is calculated and the number of voxels within the neighbourhood that + * are greater than the center voxel plus \f$ \alpha \f$ is counted. This is called the + * number of dependence voxels. The matrix gives the + * number of voxels with an intesity \f$ x \f$ and $\f d \f$ dependence neighbourhood voxels. + * + * The image is quantified prior to the calculation of the features. This reduces the number of + * available intensity values. Instead of using the pure intensity value, the features are + * calculated using the number of the bins as intensity value \f$ x_i \f$. The parameter of the + * quantification of the image can be controlled using the general binning parameters as defined + * in AbstractGlobalImageFeature. + * + * By default, the calculation is based on a 26 neighourhood for 3D and a 8 neighbourhood in 2D. It is further + * possible to exclude directions from the calculation, e.g. calculating the feature in 2D, even if a + * 3D image is passed. This is controlled by determine the + * dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature. + * + * In addition to this, the size of the neighbourhood can be controlled by setting the parameter + * ngld::range. By default it is one. To pass more than one range, separate the ranges with + * a semicolon. E.g. 1;2;3 would calculate the features for the ranges 1, 2, and 3. + * + * This feature calculator is activated by the option -neighbouring-grey-level-dependence + * or -ngld. + * + * The features are calculated based on a mask. It is assumed that the mask is + * a unsigned short image. All voxels with a value greater 0 are treated as masked. + * + * Several values are definied for the definition of the features. \f$ N_v \f$ is the number of masked voxels, + * \f$N_s \f$ is the number of neighbourhoods, \f$ m_{x,\cdot} = \sum_d m{x,d} \f$ is the number of neighbourhoods + * with a given intensity value, and likewise \f$ m_{\cdot, d} = \sum_x m{x,d} \f$ is the number of neighbourhoods + * with a given number of dependence features: + * - Neighbouring Grey Level Dependence::Low Dependence Emphasis: + * \f[ \textup{Low dependence emphasis}= \frac{1}{N_s} \sum_d { \frac{m_{\cdot, d}}{d^2} } \f] + * - Neighbouring Grey Level Dependence::High Dependence Emphasis: + * \f[ \textup{High dependence emphasis}= \frac{1}{N_s} \sum_d { m_{\cdot, d} d^2} \f] + * - Neighbouring Grey Level Dependence::Low Grey Level Count Emphasis: + * \f[ \textup{Low grey level count emphasis}= \frac{1}{N_s} \sum_x { \frac{m_{x,\cdot}}{x^2} } \f] + * - Neighbouring Grey Level Dependence::High Grey Level Count Emphasis: + * \f[ \textup{High grey level count emphasis}= \frac{1}{N_s} \sum_x { m_{x,\cdot} x^2} \f] + * - Neighbouring Grey Level Dependence::Low Dependence Low Grey Level Emphasis: + * \f[ \textup{Low Dependence Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d { \frac{m_{x,d}}{x^2 d^2} } \f] + * - Neighbouring Grey Level Dependence::Low Dependence High Grey Level Emphasis: + * \f[ \textup{Low dependence high grey level emphasis}= \frac{1}{N_s} \sum_x \sum_d { \frac{x^2 m_{x,d}}{d^2} } \f] + * - Neighbouring Grey Level Dependence::High Dependence Low Grey Level Emphasis: + * \f[ \textup{High Dependence Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d { \frac{d^2 m_{x,d}}{x^2} } \f] + * - Neighbouring Grey Level Dependence::High Dependence High Grey Level Emphasis: + * \f[ \textup{High dependence high grey level emphasis}= \frac{1}{N_s} \sum_x \sum_d { x^2 d^2 m_{x,d} } \f] + * - Neighbouring Grey Level Dependence::Grey level nonuniformity: + * \f[ \textup{Grey level nonuniformity}= \frac{1}{N_s} \sum_x m_{x,\cdot}^2 \f] + * - Neighbouring Grey Level Dependence::Grey level nonuniformity normalized: + * \f[ \textup{Grey level nonuniformity normalized}= \frac{1}{N_s^2} \sum_x m_{x,\cdot}^2 \f] + * - Neighbouring Grey Level Dependence::Dependence Count Nonuniformity: + * \f[ \textup{Dependence count nonuniformity}= \frac{1}{N_s} \sum_d m_{\cdot, d}^2 \f] + * - Neighbouring Grey Level Dependence::Dependence Count Nonuniformity Normalized: + * \f[ \textup{Dependence count nonuniformity normalized}= \frac{1}{N_s^2} \sum_d m_{\cdot, d}^2 \f] + * - Neighbouring Grey Level Dependence::DEpendence Count Percentage THe number of realized + * neighbourhoods relativ to the theoretical maximum of realized neighbourhoods. This feature is always + * one for this implementation as partial neighbourhoods are still considered. + * - Neighbouring Grey Level Dependence::Grey Level Mean: The mean value of all grey level. + * \f[ \textup{Grey Level Mean} = \mu_x = \frac{1}{N_s} \sum_x x m_{x,\cdot} \f] + * - Neighbouring Grey Level Dependence::Grey Level Variance: + * \f[ \textup{Grey level variance} = \frac{1}{N_s} \sum_x (x -mu_x)^2 m_{x, \cdot} \f] + * - Neighbouring Grey Level Dependence::Dependence Count Mean: The mean value of all dependence counts. + * \f[ \textup{Dependence count mean} = \mu_d = \frac{1}{N_s} \sum_d d m_{\cdot,d} \f] + * - Neighbouring Grey Level Dependence::Dependence Count Variance: + * \f[ \textup{Dependence count variance} = \frac{1}{N_s} \sum_d (d -mu_d)^2 m_{\cdot, d} \f] + * - Neighbouring Grey Level Dependence::Dependence Count Entropy: This feature would be equivalent with + * the Grey Level Entropy, which is therefore not included. It is based on the likelihood + * for a given intensity- size combination \f$ p_{x,d} = \frac{m_{x,d}}{N_s} \f$. : + * \f[ \textup{Dependence count entropy} = \sum_x \sum_d p_{x,d} \textup{log}_2 \left( p_{x,d} \right) \f] + * - Neighbouring Grey Level Dependence::Dependence Count Energy: This feature would be equivalent with + * the Grey Level Energy, which is therefore not included. It is based on the likelihood + * for a given intensity- size combination \f$ p_{x,d} = \frac{m_{x,d}}{N_s} \f$. : + * \f[ \textup{Dependence count energy} = \sum_x \sum_d p_{x,d}^2 \f] + * - Neighbouring Grey Level Dependence::Expected Neighbourhood Size: The expected size of a + * full neighbourhood. It depends on the dimension of the area that is looked at. + * - Neighbouring Grey Level Dependence::Average Neighbourhood Size: The feature calculation + * allows to consider partially masked neighbourhoods. Due to that, some neighbourhoods might be smaller. + * This feature gives not the theoretical neighbourhood size but the average realized neighbourhood sizes. + * - Neighbouring Grey Level Dependence::Average Incomplete Neighbourhood Size: Gives the average + * size of all neighbourhoods that are not complete. + * - Neighbouring Grey Level Dependence::Percentage of complete Neighbourhoods: Gives the percentage + * of all complete neighbourhoods from all realized neighbourhoods. + * - Neighbouring Grey Level Dependence::Percentage of Dependence Neighbour Voxels: Gives the + * percentage of voxels in all neighbourhoods compared to the expected number of voxels. + */ + class MITKCLUTILITIES_EXPORT GIFNeighbouringGreyLevelDependenceFeature : public AbstractGlobalImageFeature + { + public: + mitkClassMacro(GIFNeighbouringGreyLevelDependenceFeature, AbstractGlobalImageFeature) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + GIFNeighbouringGreyLevelDependenceFeature(); + + /** + * \brief Calculates the Cooccurence-Matrix based features for this class. + */ + virtual FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature) override; + + /** + * \brief Returns a list of the names of all features that are calculated from this class + */ + virtual FeatureNameListType GetFeatureNames() override; + + virtual std::string GetCurrentFeatureEncoding() override; + + itkGetConstMacro(Range,double); + itkSetMacro(Range, double); + itkGetConstMacro(Alpha, int); + itkSetMacro(Alpha, int); + + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + virtual void AddArguments(mitkCommandLineParser &parser); + + + struct GIFNeighbouringGreyLevelDependenceFeatureConfiguration + { + double range; + unsigned int direction; + int alpha; + + double MinimumIntensity; + double MaximumIntensity; + int Bins; + std::string FeatureEncoding; + }; + + private: + double m_Range; + int m_Alpha; + }; + +} +#endif //mitkGIFNeighbouringGreyLevelDependenceFeature_h diff --git a/Modules/Classification/CLUtilities/include/mitkGIFVolumetricDensityStatistics.h b/Modules/Classification/CLUtilities/include/mitkGIFVolumetricDensityStatistics.h new file mode 100644 index 0000000000..4099809e5f --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkGIFVolumetricDensityStatistics.h @@ -0,0 +1,132 @@ +/*=================================================================== + +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 mitkGIFVolumetricDensityStatistics_h +#define mitkGIFVolumetricDensityStatistics_h + +#include +#include +#include + +namespace mitk +{ + /** + * \brief Calculates Volumetric Density Features + * + * These features characterize the compactness of the volume and shape by comparing the volumes + * of different volume and shape estimation methods. + * + * This feature calculator is activated by the option -volume-density or -volden. + * + * The features are calculated based on a mask. It is assumed that the mask is + * of the type of an unsigned short image. All voxels with the value equal or greater than 1 are treated as masked. + * + * The volume and surface are compared to the volume \f$ V \f$ and surface \f$ A \f$ that is calculated + * directly from the mask. The following features are then defined: + * - Morphological Density::Volume density axis-aligned bounding box: The axis-aligned bounding + * box is defined as the minimum axis aligned box in 3D space that encloses all masked voxels. + * It is calculated by using the maximum spacial extension of the mask. Based on the volume of the + * bounding box, \f$ V_{aabb} \f$, the feature is defined as: + * \f[ \textup{Volume density axis-aligned bounding box}= \frac{V}{V_{aabb}} \f] + * - Morphological Density::Surface density axis-aligned bounding box: As for the previous + * feature, the axis-aligned bounding box is compared to the mask, this time using the surface of the + * bounding box \f$ A_{aabb} \f$: + * \f[ \textup{Surface density axis-aligned bounding box}= \frac{A}{A_{aabb}} \f] + * - Morphological Density::Volume density oriented minimum bounding box: A three-dimensional + * bounding box is defined using the box with the minimum volume. We do not use an estimation + * for this feature, which makes the calculation of this feature slow. Based on the volume of the + * bounding box, \f$ V_{ombb} \f$, the feature is defined as: + * \f[ \textup{Volume density oriented minimum bounding box}= \frac{V}{V_{ombb}} \f] + * - Morphological Density::Surface density axis-aligned bounding box: As for the previous + * feature, theminimum oriented bounding box is compared to the mask, this time using the surface of the + * bounding box \f$ A_{ombb} \f$: + * \f[ \textup{Surface density axis-aligned bounding box}= \frac{A}{A_{ombb}} \f] + * - Morphological Density::Volume density approx. enclosing ellipsoid: Using a Principal Component Analysis (PCA) + * of the spacial coordinates gives the three main axis of the mask. They correspond to the length of + * a eclipse enclosing the mask. The length of the axis of the eclipse are given by the eigenvalues of the + * decomposition: \f$ a = 2 \sqrt{\lambda_1} \f$, \f$ b = 2 \sqrt{\lambda_2} \f$, and \f$ c = 2 \sqrt{\lambda_3} \f$ + * with \f$\lambda_x\f$ being the sorted eigenvalues (higher number indicates larger values). The volume + * of the enclosing eclipse can be estimated by \f$ V_{aee} = 4 \pi a b c \f$: + * \f[ \textup{Volume density approx. enclosing ellipsoid}= \frac{V}{V_{aee}} \f] + * - Morphological Density::Surface density approx. enclosing ellipsoid: As for the previous + * feature, the surface of the enclosing ellipsoid is used. To simplify the calulation of it, an approximation (20 iterations) + * for the surface is used (\f$ \alpha = \sqrt{1-\frac{b^2}{a^2}} \f$, \f$ \beta = \sqrt{1-\frac{c^2}{a^2}} \f$): + * \f[ A_{aee} = 2 \pi a b \frac{\alpha^2 + \beta^2}{\alpha \beta} \sum_v^\infty \frac{(a \beta)^v}{1-a v^2} \f] + * \f[ \textup{Surface density approx. enclosing ellipsoid}= \frac{A}{A_{aee}} \f] + * - Morphological Density::Volume density approx. minimum volume enclosing ellipsoid: + * The volume is compared to the volume of the minimum enclosing ellipsoid. While this ellipsoid can be + * found by brute-force calculation, this is quite time-consuming. It is therefore estimated using + * Khachiyan's Algorithm (Khachiyan, Rounding of Polytopes in the Real Number Model of Computation. Mathematics of Operations Research 1996) + * The so-found ellipsoid is described by the lengths \f$a, b, c \f$ of its axis. The volume is then + * defined as \f$ V_{mvee} = 4 \pi a b c \f$ and the feature given by: + * \f[ \textup{Volume density approx. minimum volume enclosing ellipsoid}= \frac{V}{V_{mvee}} \f] + * - Morphological Density::Surface density approx. minimum volume enclosing ellipsoid: As for the previous + * feature, the surface of the minimum volume enclosing ellipsoid is used. To simplify the calulation of it, + * an approximation with 20 iterations instead of infinite iterations is used for the calculation of the + * the surface (\f$ \alpha = \sqrt{1-\frac{b^2}{a^2}} \f$, \f$ \beta = \sqrt{1-\frac{c^2}{a^2}} \f$): + * \f[ A_{mvee} = 2 \pi a b \frac{\alpha^2 + \beta^2}{\alpha \beta} \sum_v^\infty \frac{(a \beta)^v}{1-a v^2} \f] + * \f[ \textup{Surface density approx. minimum volume enclosing ellipsoid}= \frac{A}{A_{mvee}} \f] + * - Morphological Density::Volume density convex hull: The volume of the density + * hull is calculated using a convex mesh and then calculating the volume of this mesh \f$V_{convex} \f$. + * The feature is then calculated using: + * \f[ \textup{Volume density convex hull}= \frac{V}{V_{convex}} \f] + * - Morphological Density::Surface density convex hull: The surface of the density + * hull is calculated using a convex mesh and then calculating the surface of this mesh \f$A_{convex} \f$. + * The feature is then calculated using: + * \f[ \textup{Volume density convex hull}= \frac{A}{A_{convex}} \f] + * - Morphological Density::Volume integrated intensity: Integrated intensity is the + * average intensity times the volume. It is often used in conjunction with PET-images, where + * this feature is also called "total legion glycolysis". It is defined using the volume \f$V \f$, the + * number of masked voxels \f$ N_v \f$ and the intensity of each voxel \f$ x_i \f$: + * \f[ \textup{Volume integrated intensity}= V \frac{1}{N_v} \sum x_i \f] + * - Morphological Density::Volume Moran's I index: Moran's I index is an measure for + * the spacial autocorrelation. It is defined using the inverse spacial distance between two voxels \f$i, j \f$ \f$w_{ij} \f$, + * the number of masked voxels \f$ N_v \f$, the intensity of each voxel \f$ x_i \f$, + * and the mean intensity of all masked voxels \f$ \mu = \frac{1}{N_v} sum x_i \f$: + * \f[ \textup{Volume Moran's I index}= \frac{N_v}{\sum_i \sum_j w_{ij}} \frac{\sum_i \sum_j (x_i - \mu) (x_j -\mu)}{\sum_i (x_i - \mu)^2 } \enspace \enspace {; i \neq j} \f] + * - Morphological Density::Volume Geary's C measure: Geary's C meansure is similar to Moran's I index. + * However, it is more sensitive to grey level differences and spacial autocorrelation: + * the spacial autocorrelation. It is defined using the inverse spacial distance between two voxels \f$i, j \f$ \f$w_{ij} \f$, + * the number of masked voxels \f$ N_v \f$, the intensity of each voxel \f$ x_i \f$, + * and the mean intensity of all masked voxels \f$ \mu = \frac{1}{N_v} sum x_i \f$: + * \f[ \textup{Volume Geary's C measure}= \frac{N_v - 1}{2 \sum_i \sum_j w_{ij}} \frac{ \sum_i \sum_j w_{ij} (x_i - x_j)^2 }{\sum_i (x_i - \mu)^2 } \enspace \enspace {; i \neq j} \f] + */ + class MITKCLUTILITIES_EXPORT GIFVolumetricDensityStatistics : public AbstractGlobalImageFeature + { + public: + mitkClassMacro(GIFVolumetricDensityStatistics,AbstractGlobalImageFeature) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + GIFVolumetricDensityStatistics(); + + /** + * \brief Calculates the Cooccurence-Matrix based features for this class. + */ + virtual FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature); + + /** + * \brief Returns a list of the names of all features that are calculated from this class + */ + virtual FeatureNameListType GetFeatureNames(); + + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + virtual void AddArguments(mitkCommandLineParser &parser); + + private: + }; +} +#endif //mitkGIFVolumetricDensityStatistics_h diff --git a/Modules/Classification/CLUtilities/include/mitkGIFVolumetricStatistics.h b/Modules/Classification/CLUtilities/include/mitkGIFVolumetricStatistics.h index af18233c96..d9a89e36e1 100644 --- a/Modules/Classification/CLUtilities/include/mitkGIFVolumetricStatistics.h +++ b/Modules/Classification/CLUtilities/include/mitkGIFVolumetricStatistics.h @@ -1,52 +1,137 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkGIFVolumetricStatistics_h #define mitkGIFVolumetricStatistics_h #include #include #include namespace mitk { + /** + * \brief Calulates simpel shape-related features. + * + * This class can be used to calculate simple, shape-related features describing + * a given segmentation. There are no parameters that can be externaly set. + * + * This feature calculator is activated by the option "-volume" or "-vol" + * + * The features are calculated based on a mask. It is assumed that the mask is + * of the type of an unsigned short image and all voxels with an value larger or equal + * to one are treated as masked. (Standard MITK mask) + * + * Some of the features are calculated twice using different methods. For voxel- + * based approaches, the corresponding parameter is calcualted using the voxel, + * for example the volume is then calculated by multiplying the volume of a + * single volume with the number of voxels in the mask. In the second method, the + * mesh based appraoch, a mesh is created prior to the feature calculation which + * is then done using the features. + * + * Another difference between two features might be the evaluation of invalid + * values within the image. There are two possibilities: By default, only those + * voxels are used with an valid intensity value, i.e. where the value is not + * infinite or NaN. The second possibility is not correcting for these voxels + * and only looking at the mask. Features that use these method are marked as + * "(uncorrected)" + * + * The resulting features are: + * - Volumetric Features:: Voxel Volume: \f$ V_{single\_voxel} \f$ , the volume of an single volume, calculated as the + * multiplication of the voxel spacing in all directions. + * - Volumetric Features:: Volume (voxel based): \f$ V_{voxel} \f$, the volume of the masked area. Calulated by + * multiplying the numer of voxels with the Voxel Volume. + * - Volumetric Features:: Volume (mesh based): \f$ V_{shape} \f$, The volume based on the mesh-representation of + * the mask. + * - Volumetric Features:: Surface (voxel based): \f$ A_{voxel} \f$, the surface of the given mask. It is calulated + * by summing the surfaces between a masked and an unmasked voxel. + * - Volumetric Features:: Surface (mesh based): \f$ A_{mesh} \f$, the surface of the given mask calculated using + * the mask representation + * - Volumetric Features:: Surface to volume ration (voxel based): The ratio between voxel based surface and voxel based + * volume given as: \f[ F_{av\_voxel}=\frac{A_{voxel}}{V_{voxel}} \f] + * - Volumetric Features:: Surface to volume ration (mesh based): The ratio between voxel based surface and voxel based + * volume given as: \f[ F_{av\_mesh}=\frac{A_{mesh}}{V_{mesh}} \f] + * - Volumetric Features:: Compactness 1 (voxel based): + * - Volumetric Features:: Compactness 1 (mesh based): + The compatness is a measure how spheric a shape is given. + * Compactness 1 is defined as: + * \f[ F_{compactness\_1} = \frac{V}{\pi^{1/2} A^{3/2}}\f] + * - Volumetric Features:: Compactness 1 old (voxel based): + * - Volumetric Features:: Compactness 1 old (mesh based): Some implementations use a slightly different definition of + * compactness 1. Although this is most likely an error and leads to an non-dimensionless feature, + * this defition is still calculated as: + * \f[ F_{compactness\_1\_old} = \frac{V}{\pi^{1/2} A^{2/3}}\f] + * - Volumetric Features:: Compactness 2 (voxel based): + * - Volumetric Features:: Compactness 2 (mesh based): The compatness is a measure how spheric a shape is given. + * Compactness 2 is defined as: + * \f[ F_{compactness\_1} = 36 \pi \frac{V^2}{A^3}\f] + * - Volumetric Features::Sphericity (voxel based): + * - Volumetric Features::Sphericity (mesh based): Sphericity is measure of how sphere-like a shape is: + * \f[ F_{sphericity} = \frac{(36 \pi V^2)^{1/3}}{A} \f] + * - Volumetric Features::Asphericity (voxel based): + * - Volumetric Features::Asphericity (mesh based): Sphericity is measure of how sphere-like a shape is: + * \f[ F_{asphericity} = \left(\frac{1}{36 \pi }\frac{(A^3}{V^2}\right)^{1/3} - 1 \f] + * - Volumetric Features::Spherical disproportion (voxel based): + * - Volumetric Features::Spherical disproportion (mesh based): Sphericity is measure of how sphere-like a shape is: + * \f[ F_{spherical\_disproportion} = \frac{A}{4\pi R^2}= \frac{A}{\left(36\pi V^2\right)^{1/3}} \f] + * - Volumetric Features:: Maximum 3D diameter: This is the largest distance between the centers of two voxels that + * are masked. + * - Volumetric Features::Bounding box volume: The bounding box volume is the volume of the smallest axis-aligned box + * that encapuslates all voxel centres. + * - Volumetric Features::Centre of mass shift: + * - Volumetric Features::Centre of mass shift (uncorrected): This is the distance between two centres of mass, + * namely the geometric centre and the weighted centre. The geometric centre is the mean position + * of all masked voxels, and the weighted centre is the mean position if the position of each + * voxel is weighted according to its intensity. + * - Volumetric Features::PCA Major Axis length: + * - Volumetric Features::PCA Major Axis length (uncorrected): A Principal component analysis (PCA) of the masekd voxel + * positions will give the main orientation and elongation of the masked area. The resulting + * eigenvectors of the PCA are sorted so that \f$ \lambda_{major}\geq \lambda_{minor} \geq \lambda_{least}\f$. + * The major axis length is defined as: + * \f[ F_{pca\_major} = 4 \sqrt{\lambda_{major}} \f] + * - Volumetric Features::PCA Minor axis length: + * - Volumetric Features::PCA Minor axis length: The Minor axis length is defined as: + * \f[ F_{pca\_minor} = 4 \sqrt{\lambda_{minor}} \f] + * - Volumetric Features::PCA Least axis length: + * - Volumetric Features::PCA Least axis length: The Minor axis length is defined as: + * \f[ F_{pca\_Least} = 4 \sqrt{\lambda_{Least}} \f] + */ class MITKCLUTILITIES_EXPORT GIFVolumetricStatistics : public AbstractGlobalImageFeature { public: mitkClassMacro(GIFVolumetricStatistics,AbstractGlobalImageFeature) itkFactorylessNewMacro(Self) itkCloneMacro(Self) GIFVolumetricStatistics(); /** * \brief Calculates the Cooccurence-Matrix based features for this class. */ FeatureListType CalculateFeatures(const Image::Pointer & image, const Image::Pointer &feature) override; /** * \brief Returns a list of the names of all features that are calculated from this class */ FeatureNameListType GetFeatureNames() override; - itkGetConstMacro(Range,double); - itkSetMacro(Range, double); + virtual void CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList); + virtual void AddArguments(mitkCommandLineParser &parser); private: - double m_Range; }; } #endif //mitkGIFVolumetricStatistics_h diff --git a/Modules/Classification/CLUtilities/include/mitkGlobalImageFeaturesParameter.h b/Modules/Classification/CLUtilities/include/mitkGlobalImageFeaturesParameter.h new file mode 100644 index 0000000000..bb9da19e51 --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkGlobalImageFeaturesParameter.h @@ -0,0 +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 mitkGlobalImageFeaturesParameter_h +#define mitkGlobalImageFeaturesParameter_h + +#include "MitkCLUtilitiesExports.h" +#include "mitkCommandLineParser.h" + +#include + +namespace mitk +{ + namespace cl + { + class MITKCLUTILITIES_EXPORT GlobalImageFeaturesParameter + { + public: + void AddParameter(mitkCommandLineParser &parser); + void ParseParameter(std::map parsedArgs); + + std::string imagePath; + std::string imageName; + std::string imageFolder; + std::string maskPath; + std::string maskName; + std::string maskFolder; + std::string outputPath; + + std::string morphPath; + std::string morphName; + bool useMorphMask; + + bool useLogfile; + std::string logfilePath; + bool writeAnalysisImage; + std::string anaylsisImagePath; + bool writeAnalysisMask; + std::string analysisMaskPath; + bool writePNGScreenshots; + std::string pngScreenshotsPath; + + bool useHeader; + bool useHeaderForFirstLineOnly; + + bool ensureSameSpace; + bool resampleMask; + bool resampleToFixIsotropic; + double resampleResolution; + + bool ignoreMaskForHistogram; + bool defineGlobalMinimumIntensity; + double globalMinimumIntensity; + bool defineGlobalMaximumIntensity; + double globalMaximumIntensity; + bool defineGlobalNumberOfBins; + int globalNumberOfBins; + bool useDecimalPoint; + char decimalPoint; + bool encodeParameter; + + private: + void ParseFileLocations(std::map &parsedArgs); + void ParseAdditionalOutputs(std::map &parsedArgs); + void ParseHeaderInformation(std::map &parsedArgs); + void ParseMaskAdaptation(std::map &parsedArgs); + void ParseGlobalFeatureParameter(std::map &parsedArgs); + + }; + } +} + + + +#endif //mitkGlobalImageFeaturesParameter_h \ No newline at end of file diff --git a/Modules/Classification/CLUtilities/include/mitkRandomImageSampler.h b/Modules/Classification/CLUtilities/include/mitkRandomImageSampler.h new file mode 100644 index 0000000000..f2f11a6bbd --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkRandomImageSampler.h @@ -0,0 +1,121 @@ +/*=================================================================== + +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 __mitkRandomImageSampler_h +#define __mitkRandomImageSampler_h + +#include "MitkCLUtilitiesExports.h" + +//MITK +#include +#include "mitkImageToImageFilter.h" +#include + +namespace mitk +{ + enum RandomImageSamplerMode + { + SINGLE_ACCEPTANCE_RATE, + CLASS_DEPENDEND_ACCEPTANCE_RATE, + SINGLE_NUMBER_OF_ACCEPTANCE, + CLASS_DEPENDEND_NUMBER_OF_ACCEPTANCE + }; + + + class MITKCLUTILITIES_EXPORT RandomImageSampler : public ImageToImageFilter + { + public: + + mitkClassMacro( RandomImageSampler , ImageToImageFilter ); + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + itkSetMacro(SamplingMode, RandomImageSamplerMode); + itkGetConstMacro(SamplingMode, RandomImageSamplerMode); + + itkSetMacro(AcceptanceRate, double); + itkGetConstMacro(AcceptanceRate, double); + + //itkSetMacro(AcceptanceRateVector, std::vector); + void SetAcceptanceRateVector(std::vector arg) + { + m_AcceptanceRateVector = arg; + } + + itkGetConstMacro(AcceptanceRateVector, std::vector); + + itkSetMacro(NumberOfSamples, unsigned int); + itkGetConstMacro(NumberOfSamples, unsigned int); + + //itkSetMacro(NumberOfSamplesVector, std::vector); + void SetNumberOfSamplesVector(std::vector arg) + { + m_NumberOfSamplesVector = arg; + } + + itkGetConstMacro(NumberOfSamplesVector, std::vector); + + private: + /*! + \brief standard constructor + */ + RandomImageSampler(); + /*! + \brief standard destructor + */ + ~RandomImageSampler(); + /*! + \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. + */ + virtual void GenerateOutputInformation() override; + /*! + \brief Method generating the output of this filter. Called in the updated process of the pipeline. + This method generates the smoothed output image. + */ + virtual void GenerateData() override; + + /*! + \brief Internal templated method calling the ITK bilteral filter. Here the actual filtering is performed. + */ + template + void ItkImageProcessing(const itk::Image* itkImage); + + /*! + \brief Internal templated method calling the ITK bilteral filter. Here the actual filtering is performed. + */ + template + void ItkImageProcessingClassDependendSampling(const itk::Image* itkImage); + + /*! + \brief Internal templated method calling the ITK bilteral filter. Here the actual filtering is performed. + */ + template + void ItkImageProcessingFixedNumberSampling(const itk::Image* itkImage); + + /*! + \brief Internal templated method calling the ITK bilteral filter. Here the actual filtering is performed. + */ + template + void ItkImageProcessingClassDependendNumberSampling(const itk::Image* itkImage); + + double m_AcceptanceRate; + std::vector m_AcceptanceRateVector; + unsigned int m_NumberOfSamples; + std::vector m_NumberOfSamplesVector; + RandomImageSamplerMode m_SamplingMode; + }; +} //END mitk namespace +#endif diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.cpp b/Modules/Classification/CLUtilities/include/mitkSplitParameterToVector.h similarity index 50% copy from Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.cpp copy to Modules/Classification/CLUtilities/include/mitkSplitParameterToVector.h index 3fb6fb829a..a8903539ae 100644 --- a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.cpp +++ b/Modules/Classification/CLUtilities/include/mitkSplitParameterToVector.h @@ -1,33 +1,35 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ +#ifndef mitkSplitParameterToVector_h +#define mitkSplitParameterToVector_h -#include "org_mitk_gui_qt_classificationsegmentation_Activator.h" -#include "ClassificationSegmentation.h" +#include "MitkCLUtilitiesExports.h" +#include +#include -namespace mitk { - -void org_mitk_gui_qt_classificationsegmentation_Activator::start(ctkPluginContext* context) +namespace mitk { - BERRY_REGISTER_EXTENSION_CLASS(ClassificationSegmentation, context) + namespace cl + { + std::vector MITKCLUTILITIES_EXPORT splitDouble(std::string str, char delimiter); + std::vector MITKCLUTILITIES_EXPORT splitInt(std::string str, char delimiter); + std::vector MITKCLUTILITIES_EXPORT splitString(std::string str, char delimiter); + } } -void org_mitk_gui_qt_classificationsegmentation_Activator::stop(ctkPluginContext* context) -{ - Q_UNUSED(context) -} -} +#endif //mitkSplitParameterToVector_h \ No newline at end of file diff --git a/Modules/Classification/CLUtilities/src/Algorithms/mitkRandomImageSampler.cpp b/Modules/Classification/CLUtilities/src/Algorithms/mitkRandomImageSampler.cpp new file mode 100644 index 0000000000..feedad12b9 --- /dev/null +++ b/Modules/Classification/CLUtilities/src/Algorithms/mitkRandomImageSampler.cpp @@ -0,0 +1,266 @@ +/*=================================================================== + +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 "mitkRandomImageSampler.h" +#include +#include "mitkImageAccessByItk.h" +#include "mitkImageCast.h" +#include "itkUnaryFunctorImageFilter.h" +#include +#include "itkImageDuplicator.h" + +mitk::RandomImageSampler::RandomImageSampler() + : m_AcceptanceRate(0.1), m_SamplingMode(RandomImageSamplerMode::SINGLE_ACCEPTANCE_RATE) +{ + //default parameters DomainSigma: 2 , RangeSigma: 50, AutoKernel: true, KernelRadius: 1 +} + +mitk::RandomImageSampler::~RandomImageSampler() +{ +} + +template< class TInput, class TOutput> +class RandomlySampleFunctor +{ +public: + RandomlySampleFunctor() {}; + ~RandomlySampleFunctor() {}; + bool operator!=(const RandomlySampleFunctor &) const + { + return false; + } + bool operator==(const RandomlySampleFunctor & other) const + { + return !(*this != other); + } + inline TOutput operator()(const TInput & A) const + { + if (rand() < RAND_MAX*m_AcceptanceRate) + return A; + else + return TOutput(0); + } + + double m_AcceptanceRate = 0.1; +}; + +template< class TInput, class TOutput> +class RandomlySampleClassDependedFunctor +{ +public: + RandomlySampleClassDependedFunctor() {}; + ~RandomlySampleClassDependedFunctor() {}; + bool operator!=(const RandomlySampleClassDependedFunctor &) const + { + return false; + } + bool operator==(const RandomlySampleClassDependedFunctor & other) const + { + return !(*this != other); + } + inline TOutput operator()(const TInput & A) const + { + std::size_t index = static_cast(A + 0.5); + double samplingRate = 0; + if (index >= 0 && index < m_SamplingRateVector.size()) + { + samplingRate = m_SamplingRateVector[index]; + } + + if (rand() < RAND_MAX*samplingRate) + return A; + else + return TOutput(0); + } + + std::vector m_SamplingRateVector; +}; + +void mitk::RandomImageSampler::GenerateData() +{ + mitk::Image::ConstPointer inputImage = this->GetInput(0); + switch (m_SamplingMode) + { + case SINGLE_ACCEPTANCE_RATE: + AccessByItk(inputImage.GetPointer(), ItkImageProcessing); + break; + case CLASS_DEPENDEND_ACCEPTANCE_RATE : + AccessByItk(inputImage.GetPointer(), ItkImageProcessingClassDependendSampling); + break; + case SINGLE_NUMBER_OF_ACCEPTANCE: + AccessByItk(inputImage.GetPointer(), ItkImageProcessingFixedNumberSampling); + break; + case CLASS_DEPENDEND_NUMBER_OF_ACCEPTANCE: + AccessByItk(inputImage.GetPointer(), ItkImageProcessingClassDependendNumberSampling); + break; + default: + AccessByItk(inputImage.GetPointer(), ItkImageProcessing); + break; + } +} + +template +void mitk::RandomImageSampler::ItkImageProcessing( const itk::Image* itkImage ) +{ + //ITK Image type given from the input image + typedef itk::Image< TPixel, VImageDimension > ItkImageType; + //bilateral filter with same type + typedef RandomlySampleFunctor< typename ItkImageType::PixelType, + typename ItkImageType::PixelType> LocalSampleFunctorType; + typedef itk::UnaryFunctorImageFilter RandomImageSamplerType; + typename RandomImageSamplerType::Pointer RandomImageSampler = RandomImageSamplerType::New(); + RandomImageSampler->SetInput(itkImage); + + LocalSampleFunctorType functor; + functor.m_AcceptanceRate = m_AcceptanceRate; + RandomImageSampler->SetFunctor(functor); + RandomImageSampler->GetFunctor().m_AcceptanceRate = m_AcceptanceRate; + RandomImageSampler->Update(); + + + //get Pointer to output image + mitk::Image::Pointer resultImage = this->GetOutput(); + //write into output image + mitk::CastToMitkImage(RandomImageSampler->GetOutput(), resultImage); +} + +template +void mitk::RandomImageSampler::ItkImageProcessingClassDependendSampling(const itk::Image* itkImage) +{ + //ITK Image type given from the input image + typedef itk::Image< TPixel, VImageDimension > ItkImageType; + //bilateral filter with same type + typedef RandomlySampleClassDependedFunctor< typename ItkImageType::PixelType, + typename ItkImageType::PixelType> LocalSampleFunctorType; + typedef itk::UnaryFunctorImageFilter RandomImageSamplerType; + typename RandomImageSamplerType::Pointer RandomImageSampler = RandomImageSamplerType::New(); + RandomImageSampler->SetInput(itkImage); + + LocalSampleFunctorType functor; + functor.m_SamplingRateVector = m_AcceptanceRateVector; + RandomImageSampler->SetFunctor(functor); + RandomImageSampler->GetFunctor().m_SamplingRateVector = m_AcceptanceRateVector; + RandomImageSampler->Update(); + + + //get Pointer to output image + mitk::Image::Pointer resultImage = this->GetOutput(); + //write into output image + mitk::CastToMitkImage(RandomImageSampler->GetOutput(), resultImage); +} + +template +void mitk::RandomImageSampler::ItkImageProcessingFixedNumberSampling(const itk::Image* itkImage) +{ + //ITK Image type given from the input image + typedef itk::Image< TPixel, VImageDimension > ItkImageType; + typedef itk::ImageDuplicator< ItkImageType > DuplicatorType; + typedef itk::ImageRandomNonRepeatingIteratorWithIndex IteratorType; + + typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); + duplicator->SetInputImage(itkImage); + duplicator->Update(); + typename ItkImageType::Pointer clonedImage = duplicator->GetOutput(); + + //clonedImage->FillBuffer(0); + std::vector counts; + IteratorType iter(clonedImage, clonedImage->GetLargestPossibleRegion()); + iter.SetNumberOfSamples(clonedImage->GetLargestPossibleRegion().GetNumberOfPixels()); + //iter.GoToBegin(); + while (!iter.IsAtEnd()) + { + std::size_t index = static_cast(iter.Value() + 0.5); + while (index >= counts.size()) + { + counts.push_back(0); + } + if (counts[index] < m_NumberOfSamples) + { + //clonedImage->SetPixel(iter.GetIndex(), iter.Value()); + counts[index] += 1; + } + else + { + iter.Set(0.0); + //clonedImage->SetPixel(iter.GetIndex(), 0.0); + } + + ++iter; + } + + //get Pointer to output image + mitk::Image::Pointer resultImage = this->GetOutput(); + //write into output image + mitk::CastToMitkImage(clonedImage, resultImage); +} + +template +void mitk::RandomImageSampler::ItkImageProcessingClassDependendNumberSampling(const itk::Image* itkImage) +{ + //ITK Image type given from the input image + typedef itk::Image< TPixel, VImageDimension > ItkImageType; + typedef itk::ImageDuplicator< ItkImageType > DuplicatorType; + typedef itk::ImageRandomNonRepeatingIteratorWithIndex IteratorType; + + typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); + duplicator->SetInputImage(itkImage); + duplicator->Update(); + typename ItkImageType::Pointer clonedImage = duplicator->GetOutput(); + + std::vector counts; + IteratorType iter(clonedImage, clonedImage->GetLargestPossibleRegion()); + iter.SetNumberOfSamples(clonedImage->GetLargestPossibleRegion().GetNumberOfPixels()); + while (!iter.IsAtEnd()) + { + std::size_t index = static_cast(iter.Value() + 0.5); + if (index < m_NumberOfSamplesVector.size()) + { + while (index >= counts.size()) + { + counts.push_back(0); + } + + if (counts[index] < m_NumberOfSamplesVector[index]) + { + counts[index] += 1; + } + else + { + iter.Set(0.0); + } + } + else + { + iter.Set(0.0); + } + + ++iter; + } + + //get Pointer to output image + mitk::Image::Pointer resultImage = this->GetOutput(); + //write into output image + mitk::CastToMitkImage(clonedImage, resultImage); +} + + +void mitk::RandomImageSampler::GenerateOutputInformation() +{ + mitk::Image::Pointer inputImage = (mitk::Image*) this->GetInput(); + mitk::Image::Pointer output = this->GetOutput(); + itkDebugMacro(<<"GenerateOutputInformation()"); + if(inputImage.IsNull()) return; +} diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix.cpp index 70896f696c..3699118632 100644 --- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix.cpp +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix.cpp @@ -1,291 +1,316 @@ #include // MITK #include #include #include // ITK #include #include // STL #include template void CalculateCoocurenceFeatures(itk::Image* itkImage, mitk::Image::Pointer mask, mitk::GIFCooccurenceMatrix::FeatureListType & featureList, mitk::GIFCooccurenceMatrix::GIFCooccurenceMatrixConfiguration config) { typedef itk::Image ImageType; typedef itk::Image MaskType; typedef itk::Statistics::EnhancedScalarImageToTextureFeaturesFilter FilterType; typedef itk::MinimumMaximumImageCalculator MinMaxComputerType; typedef typename FilterType::TextureFeaturesFilterType TextureFilterType; typename MaskType::Pointer maskImage = MaskType::New(); mitk::CastToItkImage(mask, maskImage); typename FilterType::Pointer filter = FilterType::New(); typename FilterType::OffsetVector::Pointer newOffset = FilterType::OffsetVector::New(); auto oldOffsets = filter->GetOffsets(); auto oldOffsetsIterator = oldOffsets->Begin(); while(oldOffsetsIterator != oldOffsets->End()) { bool continueOuterLoop = false; typename FilterType::OffsetType offset = oldOffsetsIterator->Value(); for (unsigned int i = 0; i < VImageDimension; ++i) { offset[i] *= config.range; if (config.direction == i + 2 && offset[i] != 0) { continueOuterLoop = true; } } if (config.direction == 1) { offset[0] = 0; offset[1] = 0; offset[2] = 1; newOffset->push_back(offset); break; } oldOffsetsIterator++; if (continueOuterLoop) continue; newOffset->push_back(offset); } filter->SetOffsets(newOffset); // All features are required typename FilterType::FeatureNameVectorPointer requestedFeatures = FilterType::FeatureNameVector::New(); requestedFeatures->push_back(TextureFilterType::Energy); requestedFeatures->push_back(TextureFilterType::Entropy); requestedFeatures->push_back(TextureFilterType::Correlation); requestedFeatures->push_back(TextureFilterType::InverseDifferenceMoment); requestedFeatures->push_back(TextureFilterType::Inertia); requestedFeatures->push_back(TextureFilterType::ClusterShade); requestedFeatures->push_back(TextureFilterType::ClusterProminence); requestedFeatures->push_back(TextureFilterType::HaralickCorrelation); requestedFeatures->push_back(TextureFilterType::Autocorrelation); requestedFeatures->push_back(TextureFilterType::Contrast); requestedFeatures->push_back(TextureFilterType::Dissimilarity); requestedFeatures->push_back(TextureFilterType::MaximumProbability); requestedFeatures->push_back(TextureFilterType::InverseVariance); requestedFeatures->push_back(TextureFilterType::Homogeneity1); requestedFeatures->push_back(TextureFilterType::ClusterTendency); requestedFeatures->push_back(TextureFilterType::Variance); requestedFeatures->push_back(TextureFilterType::SumAverage); requestedFeatures->push_back(TextureFilterType::SumEntropy); requestedFeatures->push_back(TextureFilterType::SumVariance); requestedFeatures->push_back(TextureFilterType::DifferenceAverage); requestedFeatures->push_back(TextureFilterType::DifferenceEntropy); requestedFeatures->push_back(TextureFilterType::DifferenceVariance); requestedFeatures->push_back(TextureFilterType::InverseDifferenceMomentNormalized); requestedFeatures->push_back(TextureFilterType::InverseDifferenceNormalized); requestedFeatures->push_back(TextureFilterType::InverseDifference); + requestedFeatures->push_back(TextureFilterType::JointAverage); + requestedFeatures->push_back(TextureFilterType::FirstMeasureOfInformationCorrelation); + requestedFeatures->push_back(TextureFilterType::SecondMeasureOfInformationCorrelation); typename MinMaxComputerType::Pointer minMaxComputer = MinMaxComputerType::New(); minMaxComputer->SetImage(itkImage); minMaxComputer->Compute(); filter->SetInput(itkImage); filter->SetMaskImage(maskImage); filter->SetRequestedFeatures(requestedFeatures); - filter->SetPixelValueMinMax(minMaxComputer->GetMinimum()-0.5,minMaxComputer->GetMaximum()+0.5); + + double min = minMaxComputer->GetMinimum() - 0.5; + double max = minMaxComputer->GetMaximum() + 0.5; + if (config.UseMinimumIntensity) + min = config.MinimumIntensity; + if (config.UseMaximumIntensity) + max = config.MaximumIntensity; + + filter->SetPixelValueMinMax(min,max); //filter->SetPixelValueMinMax(-1024,3096); //filter->SetNumberOfBinsPerAxis(5); filter->Update(); auto featureMeans = filter->GetFeatureMeans (); auto featureStd = filter->GetFeatureStandardDeviations(); std::ostringstream ss; ss << config.range; std::string strRange = ss.str(); for (std::size_t i = 0; i < featureMeans->size(); ++i) { switch (i) { case TextureFilterType::Energy : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Energy Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Energy Std.",featureStd->ElementAt(i))); break; case TextureFilterType::Entropy : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Entropy Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Entropy Std.",featureStd->ElementAt(i))); break; case TextureFilterType::Correlation : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Correlation Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Correlation Std.",featureStd->ElementAt(i))); break; case TextureFilterType::InverseDifferenceMoment : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") InverseDifferenceMoment Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") InverseDifferenceMoment Std.",featureStd->ElementAt(i))); break; case TextureFilterType::Inertia : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Inertia Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Inertia Std.",featureStd->ElementAt(i))); break; case TextureFilterType::ClusterShade : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") ClusterShade Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") ClusterShade Std.",featureStd->ElementAt(i))); break; case TextureFilterType::ClusterProminence : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") ClusterProminence Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") ClusterProminence Std.",featureStd->ElementAt(i))); break; case TextureFilterType::HaralickCorrelation : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") HaralickCorrelation Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") HaralickCorrelation Std.",featureStd->ElementAt(i))); break; case TextureFilterType::Autocorrelation : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Autocorrelation Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Autocorrelation Std.",featureStd->ElementAt(i))); break; case TextureFilterType::Contrast : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Contrast Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Contrast Std.",featureStd->ElementAt(i))); break; case TextureFilterType::Dissimilarity : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Dissimilarity Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Dissimilarity Std.",featureStd->ElementAt(i))); break; case TextureFilterType::MaximumProbability : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") MaximumProbability Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") MaximumProbability Std.",featureStd->ElementAt(i))); break; case TextureFilterType::InverseVariance : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") InverseVariance Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") InverseVariance Std.",featureStd->ElementAt(i))); break; case TextureFilterType::Homogeneity1 : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Homogeneity1 Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Homogeneity1 Std.",featureStd->ElementAt(i))); break; case TextureFilterType::ClusterTendency : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") ClusterTendency Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") ClusterTendency Std.",featureStd->ElementAt(i))); break; case TextureFilterType::Variance : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Variance Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") Variance Std.",featureStd->ElementAt(i))); break; case TextureFilterType::SumAverage : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") SumAverage Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") SumAverage Std.",featureStd->ElementAt(i))); break; case TextureFilterType::SumEntropy : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") SumEntropy Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") SumEntropy Std.",featureStd->ElementAt(i))); break; case TextureFilterType::SumVariance : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") SumVariance Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") SumVariance Std.",featureStd->ElementAt(i))); break; case TextureFilterType::DifferenceAverage : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") DifferenceAverage Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") DifferenceAverage Std.",featureStd->ElementAt(i))); break; case TextureFilterType::DifferenceEntropy : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") DifferenceEntropy Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") DifferenceEntropy Std.",featureStd->ElementAt(i))); break; case TextureFilterType::DifferenceVariance : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") DifferenceVariance Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") DifferenceVariance Std.",featureStd->ElementAt(i))); break; case TextureFilterType::InverseDifferenceMomentNormalized : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") InverseDifferenceMomentNormalized Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") InverseDifferenceMomentNormalized Std.",featureStd->ElementAt(i))); break; case TextureFilterType::InverseDifferenceNormalized : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") InverseDifferenceNormalized Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") InverseDifferenceNormalized Std.",featureStd->ElementAt(i))); break; case TextureFilterType::InverseDifference : featureList.push_back(std::make_pair("co-occ. ("+ strRange+") InverseDifference Means",featureMeans->ElementAt(i))); featureList.push_back(std::make_pair("co-occ. ("+ strRange+") InverseDifference Std.",featureStd->ElementAt(i))); break; + case TextureFilterType::JointAverage : + featureList.push_back(std::make_pair("co-occ. ("+ strRange+") JointAverage Means",featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair("co-occ. ("+ strRange+") JointAverage Std.",featureStd->ElementAt(i))); + break; + case TextureFilterType::FirstMeasureOfInformationCorrelation : + featureList.push_back(std::make_pair("co-occ. ("+ strRange+") FirstMeasureOfInformationCorrelation Means",featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair("co-occ. ("+ strRange+") FirstMeasureOfInformationCorrelation Std.",featureStd->ElementAt(i))); + break; + case TextureFilterType::SecondMeasureOfInformationCorrelation : + featureList.push_back(std::make_pair("co-occ. ("+ strRange+") SecondMeasureOfInformationCorrelation Means",featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair("co-occ. ("+ strRange+") SecondMeasureOfInformationCorrelation Std.",featureStd->ElementAt(i))); + break; default: break; } } } mitk::GIFCooccurenceMatrix::GIFCooccurenceMatrix(): -m_Range(1.0), m_Direction(0) +m_Range(1.0) { + SetShortName("deprecated-cooc"); + SetLongName("deprecated-cooccurence"); + SetFeatureClassName("Deprecated Co-occurence Features"); } mitk::GIFCooccurenceMatrix::FeatureListType mitk::GIFCooccurenceMatrix::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) { FeatureListType featureList; GIFCooccurenceMatrixConfiguration config; - config.direction = m_Direction; + config.direction = GetDirection(); config.range = m_Range; + config.MinimumIntensity = GetMinimumIntensity(); + config.MaximumIntensity = GetMaximumIntensity(); + config.UseMinimumIntensity = GetUseMinimumIntensity(); + config.UseMaximumIntensity = GetUseMaximumIntensity(); + config.Bins = GetBins(); + AccessByItk_3(image, CalculateCoocurenceFeatures, mask, featureList,config); return featureList; } mitk::GIFCooccurenceMatrix::FeatureNameListType mitk::GIFCooccurenceMatrix::GetFeatureNames() { FeatureNameListType featureList; featureList.push_back("co-occ. Energy Means"); featureList.push_back("co-occ. Energy Std."); - featureList.push_back("co-occ. Entropy Means"); - featureList.push_back("co-occ. Entropy Std."); - featureList.push_back("co-occ. Correlation Means"); - featureList.push_back("co-occ. Correlation Std."); - featureList.push_back("co-occ. InverseDifferenceMoment Means"); - featureList.push_back("co-occ. InverseDifferenceMoment Std."); - featureList.push_back("co-occ. Inertia Means"); - featureList.push_back("co-occ. Inertia Std."); - featureList.push_back("co-occ. ClusterShade Means"); - featureList.push_back("co-occ. ClusterShade Std."); - featureList.push_back("co-occ. ClusterProminence Means"); - featureList.push_back("co-occ. ClusterProminence Std."); - featureList.push_back("co-occ. HaralickCorrelation Means"); - featureList.push_back("co-occ. HaralickCorrelation Std."); - featureList.push_back("co-occ. Autocorrelation Means"); - featureList.push_back("co-occ. Autocorrelation Std."); - featureList.push_back("co-occ. Contrast Means"); - featureList.push_back("co-occ. Contrast Std."); - featureList.push_back("co-occ. Dissimilarity Means"); - featureList.push_back("co-occ. Dissimilarity Std."); - featureList.push_back("co-occ. MaximumProbability Means"); - featureList.push_back("co-occ. MaximumProbability Std."); - featureList.push_back("co-occ. InverseVariance Means"); - featureList.push_back("co-occ. InverseVariance Std."); - featureList.push_back("co-occ. Homogeneity1 Means"); - featureList.push_back("co-occ. Homogeneity1 Std."); - featureList.push_back("co-occ. ClusterTendency Means"); - featureList.push_back("co-occ. ClusterTendency Std."); - featureList.push_back("co-occ. Variance Means"); - featureList.push_back("co-occ. Variance Std."); - featureList.push_back("co-occ. SumAverage Means"); - featureList.push_back("co-occ. SumAverage Std."); - featureList.push_back("co-occ. SumEntropy Means"); - featureList.push_back("co-occ. SumEntropy Std."); - featureList.push_back("co-occ. SumVariance Means"); - featureList.push_back("co-occ. SumVariance Std."); - featureList.push_back("co-occ. DifferenceAverage Means"); - featureList.push_back("co-occ. DifferenceAverage Std."); - featureList.push_back("co-occ. DifferenceEntropy Means"); - featureList.push_back("co-occ. DifferenceEntropy Std."); - featureList.push_back("co-occ. DifferenceVariance Means"); - featureList.push_back("co-occ. DifferenceVariance Std."); - featureList.push_back("co-occ. InverseDifferenceMomentNormalized Means"); - featureList.push_back("co-occ. InverseDifferenceMomentNormalized Std."); - featureList.push_back("co-occ. InverseDifferenceNormalized Means"); - featureList.push_back("co-occ. InverseDifferenceNormalized Std."); - featureList.push_back("co-occ. InverseDifference Means"); - featureList.push_back("co-occ. InverseDifference Std."); return featureList; -} \ No newline at end of file +} + + + +void mitk::GIFCooccurenceMatrix::AddArguments(mitkCommandLineParser &parser) +{ + std::string name = GetOptionPrefix(); + + parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Co-occurence matrix", "calculates Co-occurence based features", us::Any()); + parser.addArgument(name + "::range", name + "::range", mitkCommandLineParser::String, "Cooc 2 Range", "Define the range that is used (Semicolon-separated)", us::Any()); +} + +void +mitk::GIFCooccurenceMatrix::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &, const Image::Pointer &maskNoNAN, FeatureListType &featureList) +{ + auto parsedArgs = GetParameter(); + std::string name = GetOptionPrefix(); + + if (parsedArgs.count(GetLongName())) + { + std::vector ranges; + if (parsedArgs.count(name + "::range")) + { + ranges = SplitDouble(parsedArgs[name + "::range"].ToString(), ';'); + } + else + { + ranges.push_back(1); + } + + for (std::size_t i = 0; i < ranges.size(); ++i) + { + MITK_INFO << "Start calculating coocurence with range " << ranges[i] << "...."; + mitk::GIFCooccurenceMatrix::Pointer coocCalculator = mitk::GIFCooccurenceMatrix::New(); + coocCalculator->SetRange(ranges[i]); + auto localResults = coocCalculator->CalculateFeatures(feature, maskNoNAN); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating coocurence with range " << ranges[i] << "...."; + } + } +} + diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix2.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix2.cpp new file mode 100644 index 0000000000..be973e20be --- /dev/null +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix2.cpp @@ -0,0 +1,600 @@ +#include + +// MITK +#include +#include +#include + +// ITK +#include +#include +#include + +// STL +#include +#include + +static +void MatrixFeaturesTo(mitk::CoocurenceMatrixFeatures features, + std::string prefix, + mitk::GIFCooccurenceMatrix2::FeatureListType &featureList); +static +void CalculateMeanAndStdDevFeatures(std::vector featureList, + mitk::CoocurenceMatrixFeatures &meanFeature, + mitk::CoocurenceMatrixFeatures &stdFeature); +static +void NormalizeMatrixFeature(mitk::CoocurenceMatrixFeatures &features, + std::size_t number); + + + + +mitk::CoocurenceMatrixHolder::CoocurenceMatrixHolder(double min, double max, int number) : +m_MinimumRange(min), +m_MaximumRange(max), +m_NumberOfBins(number) +{ + m_Matrix.resize(number, number); + m_Matrix.fill(0); + m_Stepsize = (max - min) / (number); +} + +int mitk::CoocurenceMatrixHolder::IntensityToIndex(double intensity) +{ + int index = std::floor((intensity - m_MinimumRange) / m_Stepsize); + return std::max(0, std::min(index, m_NumberOfBins - 1)); +} + +double mitk::CoocurenceMatrixHolder::IndexToMinIntensity(int index) +{ + return m_MinimumRange + index * m_Stepsize; +} +double mitk::CoocurenceMatrixHolder::IndexToMeanIntensity(int index) +{ + return m_MinimumRange + (index+0.5) * m_Stepsize; +} +double mitk::CoocurenceMatrixHolder::IndexToMaxIntensity(int index) +{ + return m_MinimumRange + (index + 1) * m_Stepsize; +} + +template +void +CalculateCoOcMatrix(itk::Image* itkImage, + itk::Image* mask, + itk::Offset offset, + int range, + mitk::CoocurenceMatrixHolder &holder) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskImageType; + typedef itk::ShapedNeighborhoodIterator ShapeIterType; + typedef itk::ShapedNeighborhoodIterator ShapeMaskIterType; + typedef itk::ImageRegionConstIterator ConstIterType; + typedef itk::ImageRegionConstIterator ConstMaskIterType; + + + itk::Size radius; + radius.Fill(range+1); + ShapeIterType imageOffsetIter(radius, itkImage, itkImage->GetLargestPossibleRegion()); + ShapeMaskIterType maskOffsetIter(radius, mask, mask->GetLargestPossibleRegion()); + imageOffsetIter.ActivateOffset(offset); + maskOffsetIter.ActivateOffset(offset); + ConstIterType imageIter(itkImage, itkImage->GetLargestPossibleRegion()); + ConstMaskIterType maskIter(mask, mask->GetLargestPossibleRegion()); + // iterator.GetIndex() + ci.GetNeighborhoodOffset() + auto region = mask->GetLargestPossibleRegion(); + + + while (!maskIter.IsAtEnd()) + { + auto ciMask = maskOffsetIter.Begin(); + auto ciValue = imageOffsetIter.Begin(); + if (maskIter.Value() > 0 && + ciMask.Get() > 0 && + imageIter.Get() == imageIter.Get() && + ciValue.Get() == ciValue.Get() && + region.IsInside(maskOffsetIter.GetIndex() + ciMask.GetNeighborhoodOffset())) + { + int i = holder.IntensityToIndex(imageIter.Get()); + int j = holder.IntensityToIndex(ciValue.Get()); + holder.m_Matrix(i, j) += 1; + holder.m_Matrix(j, i) += 1; + } + ++imageOffsetIter; + ++maskOffsetIter; + ++imageIter; + ++maskIter; + } +} + +void CalculateFeatures( + mitk::CoocurenceMatrixHolder &holder, + mitk::CoocurenceMatrixFeatures & results + ) +{ + auto pijMatrix = holder.m_Matrix; + auto piMatrix = holder.m_Matrix; + auto pjMatrix = holder.m_Matrix; + double Ng = holder.m_NumberOfBins; + int NgSize = holder.m_NumberOfBins; + pijMatrix /= pijMatrix.sum(); + piMatrix.rowwise().normalize(); + pjMatrix.colwise().normalize(); + + for (int i = 0; i < holder.m_NumberOfBins; ++i) + for (int j = 0; j < holder.m_NumberOfBins; ++j) + { + if (pijMatrix(i, j) != pijMatrix(i, j)) + pijMatrix(i, j) = 0; + if (piMatrix(i, j) != piMatrix(i, j)) + piMatrix(i, j) = 0; + if (pjMatrix(i, j) != pjMatrix(i, j)) + pjMatrix(i, j) = 0; + } + + Eigen::VectorXd piVector = pijMatrix.colwise().sum(); + Eigen::VectorXd pjVector = pijMatrix.rowwise().sum(); + double sigmai = 0;; + for (int i = 0; i < holder.m_NumberOfBins; ++i) + { + double iInt = i + 1;// holder.IndexToMeanIntensity(i); + results.RowAverage += iInt * piVector(i); + if (piVector(i) > 0) + { + results.RowEntropy -= piVector(i) * std::log(piVector(i)) / std::log(2); + } + } + for (int i = 0; i < holder.m_NumberOfBins; ++i) + { + double iInt = i + 1; // holder.IndexToMeanIntensity(i); + results.RowVariance += (iInt - results.RowAverage)*(iInt - results.RowAverage) * piVector(i); + } + results.RowMaximum = piVector.maxCoeff(); + sigmai = std::sqrt(results.RowVariance); + + Eigen::VectorXd pimj(NgSize); + pimj.fill(0); + Eigen::VectorXd pipj(2*NgSize); + pipj.fill(0); + + + results.JointMaximum += pijMatrix.maxCoeff(); + + for (int i = 0; i < holder.m_NumberOfBins; ++i) + { + for (int j = 0; j < holder.m_NumberOfBins; ++j) + { + //double iInt = holder.IndexToMeanIntensity(i); + //double jInt = holder.IndexToMeanIntensity(j); + double iInt = i + 1;// holder.IndexToMeanIntensity(i); + double jInt = j + 1;// holder.IndexToMeanIntensity(j); + double pij = pijMatrix(i, j); + + int deltaK = (i - j)>0?(i-j) : (j-i); + pimj(deltaK) += pij; + pipj(i + j) += pij; + + results.JointAverage += iInt * pij; + if (pij > 0) + { + results.JointEntropy -= pij * std::log(pij) / std::log(2); + results.FirstRowColumnEntropy -= pij * std::log(piVector(i)*pjVector(j)) / std::log(2); + } + if (piVector(i) > 0 && pjVector(j) > 0 ) + { + results.SecondRowColumnEntropy -= piVector(i)*pjVector(j) * std::log(piVector(i)*pjVector(j)) / std::log(2); + } + results.AngularSecondMoment += pij*pij; + results.Contrast += (iInt - jInt)* (iInt - jInt) * pij; + results.Dissimilarity += std::abs(iInt - jInt) * pij; + results.InverseDifference += pij / (1 + (std::abs(iInt - jInt))); + results.InverseDifferenceNormalised += pij / (1 + (std::abs(iInt - jInt) / Ng)); + results.InverseDifferenceMoment += pij / (1 + (iInt - jInt)*(iInt - jInt)); + results.InverseDifferenceMomentNormalised += pij / (1 + (iInt - jInt)*(iInt - jInt)/Ng/Ng); + results.Autocorrelation += iInt*jInt * pij; + double cluster = (iInt + jInt - 2 * results.RowAverage); + results.ClusterTendency += cluster*cluster * pij; + results.ClusterShade += cluster*cluster*cluster * pij; + results.ClusterProminence += cluster*cluster*cluster*cluster * pij; + if (iInt != jInt) + { + results.InverseVariance += pij / (iInt - jInt) / (iInt - jInt); + } + } + } + results.Correlation = 1 / sigmai / sigmai * (-results.RowAverage*results.RowAverage+ results.Autocorrelation); + results.FirstMeasureOfInformationCorrelation = (results.JointEntropy - results.FirstRowColumnEntropy) / results.RowEntropy; + if (results.JointEntropy < results.SecondRowColumnEntropy) + { + results.SecondMeasureOfInformationCorrelation = sqrt(1 - exp(-2 * (results.SecondRowColumnEntropy - results.JointEntropy))); + } + else + { + results.SecondMeasureOfInformationCorrelation = 0; + } + + for (int i = 0; i < holder.m_NumberOfBins; ++i) + { + for (int j = 0; j < holder.m_NumberOfBins; ++j) + { + //double iInt = holder.IndexToMeanIntensity(i); + //double jInt = holder.IndexToMeanIntensity(j); + double iInt = i + 1; + double pij = pijMatrix(i, j); + + results.JointVariance += (iInt - results.JointAverage)* (iInt - results.JointAverage)*pij; + } + } + + for (int k = 0; k < NgSize; ++k) + { + results.DifferenceAverage += k* pimj(k); + if (pimj(k) > 0) + { + results.DifferenceEntropy -= pimj(k) * log(pimj(k)) / std::log(2); + } + } + for (int k = 0; k < NgSize; ++k) + { + results.DifferenceVariance += (results.DifferenceAverage-k)* (results.DifferenceAverage-k)*pimj(k); + } + + + for (int k = 0; k <2* NgSize ; ++k) + { + results.SumAverage += (2+k)* pipj(k); + if (pipj(k) > 0) + { + results.SumEntropy -= pipj(k) * log(pipj(k)) / std::log(2); + } + } + for (int k = 0; k < 2*NgSize; ++k) + { + results.SumVariance += (2+k - results.SumAverage)* (2+k - results.SumAverage)*pipj(k); + } + + //MITK_INFO << std::endl << holder.m_Matrix; + //MITK_INFO << std::endl << pijMatrix; + //MITK_INFO << std::endl << piMatrix; + //MITK_INFO << std::endl << pjMatrix; + + //for (int i = 0; i < holder.m_NumberOfBins; ++i) + //{ + // MITK_INFO << "Bin " << i << " Min: " << holder.IndexToMinIntensity(i) << " Max: " << holder.IndexToMaxIntensity(i); + //} + //MITK_INFO << pimj; + //MITK_INFO << pipj; + +} + +template +void +CalculateCoocurenceFeatures(itk::Image* itkImage, mitk::Image::Pointer mask, mitk::GIFCooccurenceMatrix2::FeatureListType & featureList, mitk::GIFCooccurenceMatrix2::GIFCooccurenceMatrix2Configuration config) +{ + typedef itk::Image MaskType; + typedef itk::Neighborhood NeighborhoodType; + typedef itk::Offset OffsetType; + + /////////////////////////////////////////////////////////////////////////////////////////////// + double rangeMin = config.MinimumIntensity; + double rangeMax = config.MaximumIntensity; + int numberOfBins = config.Bins; + + typename MaskType::Pointer maskImage = MaskType::New(); + mitk::CastToItkImage(mask, maskImage); + + //Find possible directions + std::vector < itk::Offset > offsetVector; + NeighborhoodType hood; + hood.SetRadius(1); + unsigned int centerIndex = hood.GetCenterNeighborhoodIndex(); + OffsetType offset; + for (unsigned int d = 0; d < centerIndex; d++) + { + offset = hood.GetOffset(d); + bool useOffset = true; + for (unsigned int i = 0; i < VImageDimension; ++i) + { + offset[i] *= config.range; + if (config.direction == i + 2 && offset[i] != 0) + { + useOffset = false; + } + } + if (useOffset) + { + offsetVector.push_back(offset); + } + } + if (config.direction == 1) + { + offsetVector.clear(); + offset[0] = 0; + offset[1] = 0; + offset[2] = 1; + } + + std::vector resultVector; + mitk::CoocurenceMatrixHolder holderOverall(rangeMin, rangeMax, numberOfBins); + mitk::CoocurenceMatrixFeatures overallFeature; + for (std::size_t i = 0; i < offsetVector.size(); ++i) + { + if (config.direction > 1) + { + if (offsetVector[i][config.direction - 2] != 0) + { + continue; + } + } + + + offset = offsetVector[i]; + mitk::CoocurenceMatrixHolder holder(rangeMin, rangeMax, numberOfBins); + mitk::CoocurenceMatrixFeatures coocResults; + CalculateCoOcMatrix(itkImage, maskImage, offset, config.range, holder); + holderOverall.m_Matrix += holder.m_Matrix; + CalculateFeatures(holder, coocResults); + resultVector.push_back(coocResults); + } + CalculateFeatures(holderOverall, overallFeature); + //NormalizeMatrixFeature(overallFeature, offsetVector.size()); + + + mitk::CoocurenceMatrixFeatures featureMean; + mitk::CoocurenceMatrixFeatures featureStd; + CalculateMeanAndStdDevFeatures(resultVector, featureMean, featureStd); + + std::ostringstream ss; + ss << config.range; + std::string strRange = ss.str(); + + MatrixFeaturesTo(overallFeature, config.prefix + " Overall", featureList); + MatrixFeaturesTo(featureMean, config.prefix + " Mean", featureList); + MatrixFeaturesTo(featureStd, config.prefix + " Std.Dev.", featureList); + + +} + + +static +void MatrixFeaturesTo(mitk::CoocurenceMatrixFeatures features, + std::string prefix, + mitk::GIFCooccurenceMatrix2::FeatureListType &featureList) +{ + featureList.push_back(std::make_pair(prefix + "Joint Maximum", features.JointMaximum)); + featureList.push_back(std::make_pair(prefix + "Joint Average", features.JointAverage)); + featureList.push_back(std::make_pair(prefix + "Joint Variance", features.JointVariance)); + featureList.push_back(std::make_pair(prefix + "Joint Entropy", features.JointEntropy)); + featureList.push_back(std::make_pair(prefix + "Row Maximum", features.RowMaximum)); + featureList.push_back(std::make_pair(prefix + "Row Average", features.RowAverage)); + featureList.push_back(std::make_pair(prefix + "Row Variance", features.RowVariance)); + featureList.push_back(std::make_pair(prefix + "Row Entropy", features.RowEntropy)); + featureList.push_back(std::make_pair(prefix + "First Row-Column Entropy", features.FirstRowColumnEntropy)); + featureList.push_back(std::make_pair(prefix + "Second Row-Column Entropy", features.SecondRowColumnEntropy)); + featureList.push_back(std::make_pair(prefix + "Difference Average", features.DifferenceAverage)); + featureList.push_back(std::make_pair(prefix + "Difference Variance", features.DifferenceVariance)); + featureList.push_back(std::make_pair(prefix + "Difference Entropy", features.DifferenceEntropy)); + featureList.push_back(std::make_pair(prefix + "Sum Average", features.SumAverage)); + featureList.push_back(std::make_pair(prefix + "Sum Variance", features.SumVariance)); + featureList.push_back(std::make_pair(prefix + "Sum Entropy", features.SumEntropy)); + featureList.push_back(std::make_pair(prefix + "Angular Second Moment", features.AngularSecondMoment)); + featureList.push_back(std::make_pair(prefix + "Contrast", features.Contrast)); + featureList.push_back(std::make_pair(prefix + "Dissimilarity", features.Dissimilarity)); + featureList.push_back(std::make_pair(prefix + "Inverse Difference", features.InverseDifference)); + featureList.push_back(std::make_pair(prefix + "Inverse Difference Normalized", features.InverseDifferenceNormalised)); + featureList.push_back(std::make_pair(prefix + "Inverse Difference Moment", features.InverseDifferenceMoment)); + featureList.push_back(std::make_pair(prefix + "Inverse Difference Moment Normalized", features.InverseDifferenceMomentNormalised)); + featureList.push_back(std::make_pair(prefix + " Inverse Variance", features.InverseVariance)); + featureList.push_back(std::make_pair(prefix + "Correlation", features.Correlation)); + featureList.push_back(std::make_pair(prefix + "Autocorrleation", features.Autocorrelation)); + featureList.push_back(std::make_pair(prefix + "Cluster Tendency", features.ClusterTendency)); + featureList.push_back(std::make_pair(prefix + "Cluster Shade", features.ClusterShade)); + featureList.push_back(std::make_pair(prefix + "Cluster Prominence", features.ClusterProminence)); + featureList.push_back(std::make_pair(prefix + "First Measure of Information Correlation", features.FirstMeasureOfInformationCorrelation)); + featureList.push_back(std::make_pair(prefix + "Second Measure of Information Correlation", features.SecondMeasureOfInformationCorrelation)); +} + +static +void CalculateMeanAndStdDevFeatures(std::vector featureList, + mitk::CoocurenceMatrixFeatures &meanFeature, + mitk::CoocurenceMatrixFeatures &stdFeature) +{ +#define ADDFEATURE(a) meanFeature.a += featureList[i].a;stdFeature.a += featureList[i].a*featureList[i].a +#define CALCVARIANCE(a) stdFeature.a =sqrt(stdFeature.a - meanFeature.a*meanFeature.a) + + for (std::size_t i = 0; i < featureList.size(); ++i) + { + ADDFEATURE(JointMaximum); + ADDFEATURE(JointAverage); + ADDFEATURE(JointVariance); + ADDFEATURE(JointEntropy); + ADDFEATURE(RowMaximum); + ADDFEATURE(RowAverage); + ADDFEATURE(RowVariance); + ADDFEATURE(RowEntropy); + ADDFEATURE(FirstRowColumnEntropy); + ADDFEATURE(SecondRowColumnEntropy); + ADDFEATURE(DifferenceAverage); + ADDFEATURE(DifferenceVariance); + ADDFEATURE(DifferenceEntropy); + ADDFEATURE(SumAverage); + ADDFEATURE(SumVariance); + ADDFEATURE(SumEntropy); + ADDFEATURE(AngularSecondMoment); + ADDFEATURE(Contrast); + ADDFEATURE(Dissimilarity); + ADDFEATURE(InverseDifference); + ADDFEATURE(InverseDifferenceNormalised); + ADDFEATURE(InverseDifferenceMoment); + ADDFEATURE(InverseDifferenceMomentNormalised); + ADDFEATURE(InverseVariance); + ADDFEATURE(Correlation); + ADDFEATURE(Autocorrelation); + ADDFEATURE(ClusterShade); + ADDFEATURE(ClusterTendency); + ADDFEATURE(ClusterProminence); + ADDFEATURE(FirstMeasureOfInformationCorrelation); + ADDFEATURE(SecondMeasureOfInformationCorrelation); + } + NormalizeMatrixFeature(meanFeature, featureList.size()); + NormalizeMatrixFeature(stdFeature, featureList.size()); + + CALCVARIANCE(JointMaximum); + CALCVARIANCE(JointAverage); + CALCVARIANCE(JointVariance); + CALCVARIANCE(JointEntropy); + CALCVARIANCE(RowMaximum); + CALCVARIANCE(RowAverage); + CALCVARIANCE(RowVariance); + CALCVARIANCE(RowEntropy); + CALCVARIANCE(FirstRowColumnEntropy); + CALCVARIANCE(SecondRowColumnEntropy); + CALCVARIANCE(DifferenceAverage); + CALCVARIANCE(DifferenceVariance); + CALCVARIANCE(DifferenceEntropy); + CALCVARIANCE(SumAverage); + CALCVARIANCE(SumVariance); + CALCVARIANCE(SumEntropy); + CALCVARIANCE(AngularSecondMoment); + CALCVARIANCE(Contrast); + CALCVARIANCE(Dissimilarity); + CALCVARIANCE(InverseDifference); + CALCVARIANCE(InverseDifferenceNormalised); + CALCVARIANCE(InverseDifferenceMoment); + CALCVARIANCE(InverseDifferenceMomentNormalised); + CALCVARIANCE(InverseVariance); + CALCVARIANCE(Correlation); + CALCVARIANCE(Autocorrelation); + CALCVARIANCE(ClusterShade); + CALCVARIANCE(ClusterTendency); + CALCVARIANCE(ClusterProminence); + CALCVARIANCE(FirstMeasureOfInformationCorrelation); + CALCVARIANCE(SecondMeasureOfInformationCorrelation); + +#undef ADDFEATURE +#undef CALCVARIANCE +} + +static +void NormalizeMatrixFeature(mitk::CoocurenceMatrixFeatures &features, + std::size_t number) +{ + features.JointMaximum = features.JointMaximum / number; + features.JointAverage = features.JointAverage / number; + features.JointVariance = features.JointVariance / number; + features.JointEntropy = features.JointEntropy / number; + features.RowMaximum = features.RowMaximum / number; + features.RowAverage = features.RowAverage / number; + features.RowVariance = features.RowVariance / number; + features.RowEntropy = features.RowEntropy / number; + features.FirstRowColumnEntropy = features.FirstRowColumnEntropy / number; + features.SecondRowColumnEntropy = features.SecondRowColumnEntropy / number; + features.DifferenceAverage = features.DifferenceAverage / number; + features.DifferenceVariance = features.DifferenceVariance / number; + features.DifferenceEntropy = features.DifferenceEntropy / number; + features.SumAverage = features.SumAverage / number; + features.SumVariance = features.SumVariance / number; + features.SumEntropy = features.SumEntropy / number; + features.AngularSecondMoment = features.AngularSecondMoment / number; + features.Contrast = features.Contrast / number; + features.Dissimilarity = features.Dissimilarity / number; + features.InverseDifference = features.InverseDifference / number; + features.InverseDifferenceNormalised = features.InverseDifferenceNormalised / number; + features.InverseDifferenceMoment = features.InverseDifferenceMoment / number; + features.InverseDifferenceMomentNormalised = features.InverseDifferenceMomentNormalised / number; + features.InverseVariance = features.InverseVariance / number; + features.Correlation = features.Correlation / number; + features.Autocorrelation = features.Autocorrelation / number; + features.ClusterShade = features.ClusterShade / number; + features.ClusterTendency = features.ClusterTendency / number; + features.ClusterProminence = features.ClusterProminence / number; + features.FirstMeasureOfInformationCorrelation = features.FirstMeasureOfInformationCorrelation / number; + features.SecondMeasureOfInformationCorrelation = features.SecondMeasureOfInformationCorrelation / number; +} + +mitk::GIFCooccurenceMatrix2::GIFCooccurenceMatrix2(): +m_Range(1.0) +{ + SetShortName("cooc2"); + SetLongName("cooccurence2"); + SetFeatureClassName("Co-occurenced Based Features"); +} + +mitk::GIFCooccurenceMatrix2::FeatureListType mitk::GIFCooccurenceMatrix2::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) +{ + InitializeQuantifier(image, mask); + + FeatureListType featureList; + + GIFCooccurenceMatrix2Configuration config; + config.direction = GetDirection(); + config.range = m_Range; + + config.MinimumIntensity = GetQuantifier()->GetMinimum(); + config.MaximumIntensity = GetQuantifier()->GetMaximum(); + config.Bins = GetQuantifier()->GetBins(); + config.prefix = FeatureDescriptionPrefix(); + + AccessByItk_3(image, CalculateCoocurenceFeatures, mask, featureList,config); + + return featureList; +} + +mitk::GIFCooccurenceMatrix2::FeatureNameListType mitk::GIFCooccurenceMatrix2::GetFeatureNames() +{ + FeatureNameListType featureList; + return featureList; +} + + + + +void mitk::GIFCooccurenceMatrix2::AddArguments(mitkCommandLineParser &parser) +{ + std::string name = GetOptionPrefix(); + + parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Co-occurence matrix", "calculates Co-occurence based features (new implementation)", us::Any()); + parser.addArgument(name+"::range", name+"::range", mitkCommandLineParser::String, "Cooc 2 Range", "Define the range that is used (Semicolon-separated)", us::Any()); + AddQuantifierArguments(parser); +} + +void +mitk::GIFCooccurenceMatrix2::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &, const Image::Pointer &maskNoNAN, FeatureListType &featureList) +{ + auto parsedArgs = GetParameter(); + std::string name = GetOptionPrefix(); + + if (parsedArgs.count(GetLongName())) + { + InitializeQuantifierFromParameters(feature, maskNoNAN); + std::vector ranges; + + if (parsedArgs.count(name + "::range")) + { + ranges = SplitDouble(parsedArgs[name + "::range"].ToString(), ';'); + } + else + { + ranges.push_back(1); + } + + for (std::size_t i = 0; i < ranges.size(); ++i) + { + MITK_INFO << "Start calculating coocurence with range " << ranges[i] << "...."; + this->SetRange(ranges[i]); + auto localResults = this->CalculateFeatures(feature, maskNoNAN); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating coocurence with range " << ranges[i] << "...."; + } + } +} + + +std::string mitk::GIFCooccurenceMatrix2::GetCurrentFeatureEncoding() +{ + std::ostringstream ss; + ss << m_Range; + std::string strRange = ss.str(); + return QuantifierParameterString() + "_Range-" + ss.str(); +} diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCurvatureStatistic.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCurvatureStatistic.cpp new file mode 100644 index 0000000000..7ab9963061 --- /dev/null +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCurvatureStatistic.cpp @@ -0,0 +1,181 @@ +/*=================================================================== + +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 + +// MITK +#include +#include +#include +#include +#include + +// ITK +#include +#include + +// VTK +#include +#include +#include +#include + +// STL +#include +#include + +static void calculateLocalStatistic(vtkDataArray* scalars, std::string name, std::string featureDescriptionPrefix, mitk::GIFCurvatureStatistic::FeatureListType & featureList) +{ + int size = scalars->GetNumberOfTuples(); + double minimum = std::numeric_limits::max(); + double maximum = std::numeric_limits::lowest(); + double mean1 = 0; + double mean2 = 0; + double mean3 = 0; + double mean4 = 0; + double mean1p = 0; + double mean2p = 0; + double mean3p = 0; + double mean4p = 0; + double mean1n = 0; + double mean2n = 0; + double mean3n = 0; + double mean4n = 0; + int countPositive = 0; + int countNegative = 0; + + for (int i = 0; i < size; ++i) + { + double actualValue = scalars->GetComponent(i, 0); + minimum = std::min(minimum, scalars->GetComponent(i, 0)); + maximum = std::max(maximum, scalars->GetComponent(i, 0)); + mean1 += actualValue; + mean2 += actualValue*actualValue; + mean3 += actualValue*actualValue*actualValue; + mean4 += actualValue*actualValue*actualValue*actualValue; + if (actualValue > 0) + { + mean1p += actualValue; + mean2p += actualValue*actualValue; + mean3p += actualValue*actualValue*actualValue; + mean4p += actualValue*actualValue*actualValue*actualValue; + countPositive++; + } + if (actualValue < 0) + { + mean1n += actualValue; + mean2n += actualValue*actualValue; + mean3n += actualValue*actualValue*actualValue; + mean4n += actualValue*actualValue*actualValue*actualValue; + countNegative++; + } + } + double mean = mean1 / size; + double stddev = std::sqrt(mean2 / size - mean*mean); + double skewness = ((mean3 / size) - 3 * mean*stddev*stddev - mean*mean*mean) / (stddev*stddev*stddev); + + double meanP = mean1p / countPositive; + double stddevP = std::sqrt(mean2p / countPositive - meanP*meanP); + double skewnessP = ((mean3p / countPositive) - 3 * meanP*stddevP*stddevP - meanP*meanP*meanP) / (stddevP*stddevP*stddevP); + + double meanN = mean1n / countNegative; + double stddevN = std::sqrt(mean2n / countNegative - meanN*meanN); + double skewnessN = ((mean3n / countNegative) - 3 * meanN*stddevN*stddevN - meanN*meanN*meanN) / (stddevN*stddevN*stddevN); + + featureList.push_back(std::make_pair(featureDescriptionPrefix + "Minimum " + name + " Curvature", minimum)); + featureList.push_back(std::make_pair(featureDescriptionPrefix + "Maximum " + name + " Curvature", maximum)); + featureList.push_back(std::make_pair(featureDescriptionPrefix + "Mean " + name + " Curvature", mean)); + featureList.push_back(std::make_pair(featureDescriptionPrefix + "Standard Deviation " + name + " Curvature", stddev)); + featureList.push_back(std::make_pair(featureDescriptionPrefix + "Skewness " + name + " Curvature", skewness)); + featureList.push_back(std::make_pair(featureDescriptionPrefix + "Mean Positive " + name + " Curvature", meanP)); + featureList.push_back(std::make_pair(featureDescriptionPrefix + "Standard Deviation Positive " + name + " Curvature", stddevP)); + featureList.push_back(std::make_pair(featureDescriptionPrefix + "Skewness Positive " + name + " Curvature", skewnessP)); + featureList.push_back(std::make_pair(featureDescriptionPrefix + "Mean Negative " + name + " Curvature", meanN)); + featureList.push_back(std::make_pair(featureDescriptionPrefix + "Standard Deviation Negative " + name + " Curvature", stddevN)); + featureList.push_back(std::make_pair(featureDescriptionPrefix + "Skewness Negative " + name + " Curvature", skewnessN)); + +} + +mitk::GIFCurvatureStatistic::GIFCurvatureStatistic() +{ + SetLongName("curvature"); + SetShortName("cur"); + SetFeatureClassName("Curvature Feature"); +} + +mitk::GIFCurvatureStatistic::FeatureListType mitk::GIFCurvatureStatistic::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) +{ + FeatureListType featureList; + if (image->GetDimension() < 3) + { + return featureList; + } + + vtkSmartPointer mesher = vtkSmartPointer::New(); + vtkSmartPointer curvator = vtkSmartPointer::New(); + mesher->SetInputData(mask->GetVtkImageData()); + mesher->SetValue(0, 0.5); + curvator->SetInputConnection(mesher->GetOutputPort()); + curvator->SetCurvatureTypeToMean(); + curvator->Update(); + vtkDataArray* scalars = curvator->GetOutput()->GetPointData()->GetScalars(); + calculateLocalStatistic(scalars, "Mean", FeatureDescriptionPrefix(), featureList); + + curvator->SetCurvatureTypeToGaussian(); + curvator->Update(); + scalars = curvator->GetOutput()->GetPointData()->GetScalars(); + calculateLocalStatistic(scalars, "Gaussian", FeatureDescriptionPrefix(), featureList); + + curvator->SetCurvatureTypeToMinimum(); + curvator->Update(); + scalars = curvator->GetOutput()->GetPointData()->GetScalars(); + calculateLocalStatistic(scalars, "Minimum", FeatureDescriptionPrefix(), featureList); + + curvator->SetCurvatureTypeToMaximum(); + curvator->Update(); + scalars = curvator->GetOutput()->GetPointData()->GetScalars(); + calculateLocalStatistic(scalars, "Maximum", FeatureDescriptionPrefix(), featureList); + + return featureList; +} + +mitk::GIFCurvatureStatistic::FeatureNameListType mitk::GIFCurvatureStatistic::GetFeatureNames() +{ + FeatureNameListType featureList; + return featureList; +} + + +void mitk::GIFCurvatureStatistic::AddArguments(mitkCommandLineParser &parser) +{ + std::string name = GetOptionPrefix(); + + parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Curvature of Surface as feature", "calculates shape curvature based features", us::Any()); +} + +void +mitk::GIFCurvatureStatistic::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &, FeatureListType &featureList) +{ + auto parsedArgs = GetParameter(); + if (parsedArgs.count(GetLongName())) + { + MITK_INFO << "Start calculating volumetric features ...."; + auto localResults = this->CalculateFeatures(feature, mask); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating volumetric features...."; + } +} + diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderHistogramStatistics.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderHistogramStatistics.cpp new file mode 100644 index 0000000000..3103ffadfa --- /dev/null +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderHistogramStatistics.cpp @@ -0,0 +1,337 @@ +/*=================================================================== + +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 + +// MITK +#include +#include +#include + +// ITK +#include +#include +#include + +// STL +#include +#include +#include + +#define GET_VARIABLE_INDEX(value) \ + mv[0] = value; \ + histogram->GetIndex(mv, resultingIndex); + + +template +void +CalculateFirstOrderHistogramStatistics(itk::Image* itkImage, mitk::Image::Pointer mask, mitk::GIFFirstOrderHistogramStatistics::FeatureListType & featureList, mitk::GIFFirstOrderHistogramStatistics::ParameterStruct params) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskType; + typedef itk::LabelStatisticsImageFilter FilterType; + typedef typename FilterType::HistogramType HistogramType; + typedef typename HistogramType::IndexType HIndexType; + + typename MaskType::Pointer maskImage = MaskType::New(); + mitk::CastToItkImage(mask, maskImage); + + typename FilterType::Pointer labelStatisticsImageFilter = FilterType::New(); + labelStatisticsImageFilter->SetInput(itkImage); + labelStatisticsImageFilter->SetLabelInput(maskImage); + labelStatisticsImageFilter->SetUseHistograms(true); + labelStatisticsImageFilter->SetHistogramParameters(params.Bins, params.MinimumIntensity, params.MaximumIntensity); + + labelStatisticsImageFilter->Update(); + + typename HistogramType::MeasurementVectorType mv(1); + mv[0] = 4.1; + typename HistogramType::IndexType resultingIndex; + + auto histogram = labelStatisticsImageFilter->GetHistogram(1); + double meanValue = 0; // labelStatisticsImageFilter->GetMean(1); + GET_VARIABLE_INDEX(meanValue); + double meanIndex = 0; // resultingIndex[0]; + double medianValue = labelStatisticsImageFilter->GetMedian(1); + GET_VARIABLE_INDEX(medianValue); + double medianIndex = resultingIndex[0]; + double minimumValue = labelStatisticsImageFilter->GetMinimum(1); + GET_VARIABLE_INDEX(minimumValue); + double minimumIndex = resultingIndex[0]; + double p10Value = histogram->Quantile(0, 0.10); + GET_VARIABLE_INDEX(p10Value); + double p10Index = resultingIndex[0]; + double p25Value = histogram->Quantile(0, 0.25); + GET_VARIABLE_INDEX(p25Value); + double p25Index = resultingIndex[0]; + double p75Value = histogram->Quantile(0, 0.75); + GET_VARIABLE_INDEX(p75Value); + double p75Index = resultingIndex[0]; + double p90Value = histogram->Quantile(0, 0.90); + GET_VARIABLE_INDEX(p90Value); + double p90Index = resultingIndex[0]; + double maximumValue = labelStatisticsImageFilter->GetMaximum(1); + GET_VARIABLE_INDEX(maximumValue); + double maximumIndex = resultingIndex[0]; + + double Log2 = log(2); + HIndexType index; + HIndexType index2; + index.SetSize(1); + index2.SetSize(1); + double binWidth = histogram->GetBinMax(0, 0) - histogram->GetBinMin(0, 0); + double count = labelStatisticsImageFilter->GetCount(1); + + double robustMeanValue = 0; + double robustMeanIndex = 0; + double robustCount = 0; + + for (int i = 0; i < (int)(histogram->GetSize(0)); ++i) + { + index[0] = i; + double frequence = histogram->GetFrequency(index); + double probability = frequence / count; + double voxelValue = histogram->GetBinMin(0, i) + binWidth * 0.5; + + meanValue += probability * voxelValue; + meanIndex += probability * (i); + + if ((i >= p10Index) && (i <= p90Index)) + { + robustMeanValue += frequence * voxelValue; + robustMeanIndex += frequence * i; + robustCount += frequence; + } + } + robustMeanValue /= robustCount; + robustMeanIndex /= robustCount; + + double varianceValue = 0; + double varianceIndex = 0; + double skewnessValue = 0; + double skewnessIndex = 0; + double kurtosisValue = 0; + double kurtosisIndex = 0; + double modeValue = 0; + double modeIndex = 0; + double modeFrequence = 0; + double meanAbsoluteDeviationValue = 0; + double meanAbsoluteDeviationIndex = 0; + double robustMeanAbsoluteDeviationValue = 0; + double robustMeanAbsoluteDeivationIndex = 0; + double medianAbsoluteDeviationValue = 0; + double medianAbsoluteDeviationIndex = 0; + double coefficientOfVariationValue = 0; + double coefficientOfVariationIndex = 0; + double quantileCoefficientOfDispersionValue = 0; + double quantileCoefficientOfDispersionIndex = 0; + double entropyValue = 0; + double entropyIndex = 0; + double uniformityValue = 0; + double uniformityIndex = 0; + + double maximumGradientValue = std::numeric_limits::min(); + double maximumGradientIndex = 0; + double minimumGradientValue = std::numeric_limits::max(); + double minimumGradientIndex = 0; + + double gradient = 0; + + for (int i = 0; i < (int)(histogram->GetSize(0)); ++i) + { + index[0] = i; + double frequence = histogram->GetFrequency(index); + double probability = frequence / count; + double voxelValue = histogram->GetBinMin(0, i) + binWidth * 0.5; + + double deltaValue = (voxelValue - meanValue); + double deltaIndex = (i - meanIndex); + + varianceValue += probability * deltaValue * deltaValue; + varianceIndex += probability * deltaIndex * deltaIndex; + skewnessValue += probability * deltaValue * deltaValue * deltaValue; + skewnessIndex += probability * deltaIndex * deltaIndex * deltaIndex; + kurtosisValue += probability * deltaValue * deltaValue * deltaValue * deltaValue; + kurtosisIndex += probability * deltaIndex * deltaIndex * deltaIndex * deltaIndex; + + if (modeFrequence < frequence) + { + modeFrequence = frequence; + modeValue = voxelValue; + modeIndex = i; + } + meanAbsoluteDeviationValue += probability * std::abs(deltaValue); + meanAbsoluteDeviationIndex += probability * std::abs(deltaIndex); + if ((i >= p10Index) && (i <= p90Index)) + { + robustMeanAbsoluteDeviationValue += frequence * std::abs(voxelValue - robustMeanValue); + robustMeanAbsoluteDeivationIndex += frequence * std::abs(i*1.0 - robustMeanIndex*1.0); + } + medianAbsoluteDeviationValue += probability * std::abs(voxelValue - medianValue); + medianAbsoluteDeviationIndex += probability * std::abs(i*1.0 - medianIndex); + if (probability > 0.0000001) + { + entropyValue -= probability * std::log(probability) / Log2; + entropyIndex = entropyValue; + } + uniformityValue += probability*probability; + uniformityIndex = uniformityValue; + if (i == 0) + { + index[0] = 1; index2[0] = 0; + gradient = histogram->GetFrequency(index)*1.0 - histogram->GetFrequency(index2)*1.0; + } + else if (i == (int)(histogram->GetSize(0)) - 1) + { + index[0] = i; index2[0] = i - 1; + gradient = histogram->GetFrequency(index)*1.0 - histogram->GetFrequency(index2)*1.0; + } + else + { + index[0] = i+1; index2[0] = i - 1; + gradient = (histogram->GetFrequency(index)*1.0 - histogram->GetFrequency(index2)*1.0) / 2.0; + } + if (gradient > maximumGradientValue) + { + maximumGradientValue = gradient; + maximumGradientIndex = i + 1; + } + if (gradient < minimumGradientValue) + { + minimumGradientValue = gradient; + minimumGradientIndex = i + 1; + } + } + skewnessValue = skewnessValue / (varianceValue * std::sqrt(varianceValue)); + skewnessIndex = skewnessIndex / (varianceIndex * std::sqrt(varianceIndex)); + kurtosisValue = kurtosisValue / (varianceValue * varianceValue) - 3; // Excess Kurtosis + kurtosisIndex = kurtosisIndex / (varianceIndex * varianceIndex) - 3; // Excess Kurtosis + coefficientOfVariationValue = std::sqrt(varianceValue) / meanValue; + coefficientOfVariationIndex = std::sqrt(varianceIndex) / (meanIndex+1); + quantileCoefficientOfDispersionValue = (p75Value - p25Value) / (p75Value + p25Value); + quantileCoefficientOfDispersionIndex = (p75Index - p25Index) / (p75Index + p25Index); + robustMeanAbsoluteDeviationValue /= robustCount; + robustMeanAbsoluteDeivationIndex /= robustCount; + + featureList.push_back(std::make_pair(params.prefix + "Mean Value", meanValue)); + featureList.push_back(std::make_pair(params.prefix + "Variance Value", varianceValue)); + featureList.push_back(std::make_pair(params.prefix + "Skewness Value", skewnessValue)); + featureList.push_back(std::make_pair(params.prefix + "Excess Kurtosis Value", kurtosisValue)); + featureList.push_back(std::make_pair(params.prefix + "Median Value", medianValue)); + featureList.push_back(std::make_pair(params.prefix + "Minimum Value", minimumValue)); + featureList.push_back(std::make_pair(params.prefix + "Percentile 10 Value", p10Value)); + featureList.push_back(std::make_pair(params.prefix + "Percentile 90 Value", p90Value)); + featureList.push_back(std::make_pair(params.prefix + "Maximum Value", maximumValue)); + featureList.push_back(std::make_pair(params.prefix + "Mode Value", modeValue)); + featureList.push_back(std::make_pair(params.prefix + "Interquantile Range Value", p75Value - p25Value)); + featureList.push_back(std::make_pair(params.prefix + "Range Value", maximumValue - minimumValue)); + featureList.push_back(std::make_pair(params.prefix + "Mean Absolute Deviation Value", meanAbsoluteDeviationValue)); + featureList.push_back(std::make_pair(params.prefix + "Robust Mean Absolute Deviation Value", robustMeanAbsoluteDeviationValue)); + featureList.push_back(std::make_pair(params.prefix + "Median Absolute Deviation Value", medianAbsoluteDeviationValue)); + featureList.push_back(std::make_pair(params.prefix + "Coefficient of Variation Value", coefficientOfVariationValue)); + featureList.push_back(std::make_pair(params.prefix + "Quantile coefficient of Dispersion Value", quantileCoefficientOfDispersionValue)); + featureList.push_back(std::make_pair(params.prefix + "Entropy Value", entropyValue)); + featureList.push_back(std::make_pair(params.prefix + "Uniformity Value", uniformityValue)); + featureList.push_back(std::make_pair(params.prefix + "Robust Mean Value", robustMeanValue)); + + featureList.push_back(std::make_pair(params.prefix + "Mean Index", meanIndex + 1 )); + featureList.push_back(std::make_pair(params.prefix + "Variance Index", varianceIndex)); + featureList.push_back(std::make_pair(params.prefix + "Skewness Index", skewnessIndex)); + featureList.push_back(std::make_pair(params.prefix + "Excess Kurtosis Index", kurtosisIndex)); + featureList.push_back(std::make_pair(params.prefix + "Median Index", medianIndex + 1)); + featureList.push_back(std::make_pair(params.prefix + "Minimum Index", minimumIndex + 1)); + featureList.push_back(std::make_pair(params.prefix + "Percentile 10 Index", p10Index + 1)); + featureList.push_back(std::make_pair(params.prefix + "Percentile 90 Index", p90Index + 1)); + featureList.push_back(std::make_pair(params.prefix + "Maximum Index", maximumIndex + 1)); + featureList.push_back(std::make_pair(params.prefix + "Mode Index", modeIndex + 1)); + featureList.push_back(std::make_pair(params.prefix + "Interquantile Range Index", p75Index - p25Index)); + featureList.push_back(std::make_pair(params.prefix + "Range Index", maximumIndex - minimumIndex)); + featureList.push_back(std::make_pair(params.prefix + "Mean Absolute Deviation Index", meanAbsoluteDeviationIndex)); + featureList.push_back(std::make_pair(params.prefix + "Robust Mean Absolute Deviation Index", robustMeanAbsoluteDeivationIndex)); + featureList.push_back(std::make_pair(params.prefix + "Median Absolute Deviation Index", medianAbsoluteDeviationIndex)); + featureList.push_back(std::make_pair(params.prefix + "Coefficient of Variation Index", coefficientOfVariationIndex)); + featureList.push_back(std::make_pair(params.prefix + "Quantile coefficient of Dispersion Index", quantileCoefficientOfDispersionIndex)); + featureList.push_back(std::make_pair(params.prefix + "Entropy Index", entropyIndex)); + featureList.push_back(std::make_pair(params.prefix + "Uniformity Index", uniformityIndex)); + featureList.push_back(std::make_pair(params.prefix + "Maximum Gradient", maximumGradientValue)); + featureList.push_back(std::make_pair(params.prefix + "Maximum Gradient Index", maximumGradientIndex)); + featureList.push_back(std::make_pair(params.prefix + "Minimum Gradient", minimumGradientValue)); + featureList.push_back(std::make_pair(params.prefix + "Minimum Gradient Index", minimumGradientIndex)); + featureList.push_back(std::make_pair(params.prefix + "Robust Mean Index", robustMeanIndex)); + + featureList.push_back(std::make_pair(params.prefix + "Number of Bins", histogram->GetSize(0))); + featureList.push_back(std::make_pair(params.prefix + "Bin Size", binWidth)); +} + +mitk::GIFFirstOrderHistogramStatistics::GIFFirstOrderHistogramStatistics() : +m_HistogramSize(256), m_UseCtRange(false), m_BinSize(-1) +{ + SetShortName("foh"); + SetLongName("first-order-histogram"); + SetFeatureClassName("First Order Histogram"); +} + +mitk::GIFFirstOrderHistogramStatistics::FeatureListType mitk::GIFFirstOrderHistogramStatistics::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) +{ + InitializeQuantifier(image, mask); + FeatureListType featureList; + + ParameterStruct params; + params.MinimumIntensity = GetQuantifier()->GetMinimum(); + params.MaximumIntensity = GetQuantifier()->GetMaximum(); + params.Bins = GetQuantifier()->GetBins(); + params.prefix = FeatureDescriptionPrefix(); + + AccessByItk_3(image, CalculateFirstOrderHistogramStatistics, mask, featureList, params); + + return featureList; +} + +mitk::GIFFirstOrderHistogramStatistics::FeatureNameListType mitk::GIFFirstOrderHistogramStatistics::GetFeatureNames() +{ + FeatureNameListType featureList; + return featureList; +} + + +void mitk::GIFFirstOrderHistogramStatistics::AddArguments(mitkCommandLineParser &parser) +{ + AddQuantifierArguments(parser); + std::string name = GetOptionPrefix(); + + parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Histogram based First order features", "calculates first order features based on a histogram", us::Any()); +} + +void +mitk::GIFFirstOrderHistogramStatistics::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList) +{ + std::string name = GetOptionPrefix(); + auto parsedArgs = GetParameter(); + if (parsedArgs.count(GetLongName())) + { + InitializeQuantifierFromParameters(feature, mask); + + MITK_INFO << "Start calculating first order histogram features ...."; + auto localResults = this->CalculateFeatures(feature, maskNoNAN); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating first order histogram features...."; + } +} +std::string mitk::GIFFirstOrderHistogramStatistics::GetCurrentFeatureEncoding() +{ + return QuantifierParameterString(); +} + diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderStatistics.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderStatistics.cpp index b35e6afa7f..448375d362 100644 --- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderStatistics.cpp +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderStatistics.cpp @@ -1,165 +1,327 @@ /*=================================================================== 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 // MITK #include #include #include // ITK #include #include // STL #include template void - CalculateFirstOrderStatistics(itk::Image* itkImage, mitk::Image::Pointer mask, mitk::GIFFirstOrderStatistics::FeatureListType & featureList, mitk::GIFFirstOrderStatistics::ParameterStruct params) +CalculateFirstOrderStatistics(itk::Image* itkImage, mitk::Image::Pointer mask, mitk::GIFFirstOrderStatistics::FeatureListType & featureList, mitk::GIFFirstOrderStatistics::ParameterStruct params) { typedef itk::Image ImageType; - typedef itk::Image MaskType; + typedef itk::Image MaskType; typedef itk::LabelStatisticsImageFilter FilterType; typedef typename FilterType::HistogramType HistogramType; typedef typename HistogramType::IndexType HIndexType; typedef itk::MinimumMaximumImageCalculator MinMaxComputerType; typename MaskType::Pointer maskImage = MaskType::New(); mitk::CastToItkImage(mask, maskImage); + double voxelVolume = 1; + for (unsigned int i = 0; i < std::min(3, VImageDimension); ++i) + voxelVolume *= itkImage->GetSpacing()[i]; + double voxelSpace = 1; + for (unsigned int i = 0; i < VImageDimension; ++i) + voxelSpace *= itkImage->GetSpacing()[i]; + typename MinMaxComputerType::Pointer minMaxComputer = MinMaxComputerType::New(); minMaxComputer->SetImage(itkImage); minMaxComputer->Compute(); double imageRange = minMaxComputer->GetMaximum() - minMaxComputer->GetMinimum(); typename FilterType::Pointer labelStatisticsImageFilter = FilterType::New(); labelStatisticsImageFilter->SetInput( itkImage ); labelStatisticsImageFilter->SetLabelInput(maskImage); labelStatisticsImageFilter->SetUseHistograms(true); - if (params.m_UseCtRange) - { - labelStatisticsImageFilter->SetHistogramParameters(1024.5+3096.5, -1024.5,3096.5); - } else { - labelStatisticsImageFilter->SetHistogramParameters(params.m_HistogramSize, minMaxComputer->GetMinimum(),minMaxComputer->GetMaximum()); - } + + double min = params.MinimumIntensity; + double max = params.MaximumIntensity; + + labelStatisticsImageFilter->SetHistogramParameters(params.Bins, min,max); labelStatisticsImageFilter->Update(); // --------------- Range -------------------- double range = labelStatisticsImageFilter->GetMaximum(1) - labelStatisticsImageFilter->GetMinimum(1); // --------------- Uniformity, Entropy -------------------- double count = labelStatisticsImageFilter->GetCount(1); //double std_dev = labelStatisticsImageFilter->GetSigma(1); - double uncorrected_std_dev = std::sqrt((count - 1) / count * labelStatisticsImageFilter->GetVariance(1)); double mean = labelStatisticsImageFilter->GetMean(1); + double median = labelStatisticsImageFilter->GetMedian(1); auto histogram = labelStatisticsImageFilter->GetHistogram(1); + bool histogramIsCalculated = histogram; + HIndexType index; index.SetSize(1); - double binWidth = histogram->GetBinMax(0, 0) - histogram->GetBinMin(0, 0); double uniformity = 0; double entropy = 0; double squared_sum = 0; double kurtosis = 0; double mean_absolut_deviation = 0; + double median_absolut_deviation = 0; double skewness = 0; double sum_prob = 0; + double binWidth = 0; + double p05th = 0, p10th = 0, p15th = 0, p20th = 0, p25th = 0, p30th = 0, p35th = 0, p40th = 0, p45th = 0, p50th = 0; + double p55th = 0, p60th = 0, p65th = 0, p70th = 0, p75th = 0, p80th = 0, p85th = 0, p90th = 0, p95th = 0; + + double voxelValue = 0; + if (histogramIsCalculated) + { + binWidth = histogram->GetBinMax(0, 0) - histogram->GetBinMin(0, 0); + p05th = histogram->Quantile(0, 0.05); + p10th = histogram->Quantile(0, 0.10); + p15th = histogram->Quantile(0, 0.15); + p20th = histogram->Quantile(0, 0.20); + p25th = histogram->Quantile(0, 0.25); + p30th = histogram->Quantile(0, 0.30); + p35th = histogram->Quantile(0, 0.35); + p40th = histogram->Quantile(0, 0.40); + p45th = histogram->Quantile(0, 0.45); + p50th = histogram->Quantile(0, 0.50); + p55th = histogram->Quantile(0, 0.55); + p60th = histogram->Quantile(0, 0.60); + p65th = histogram->Quantile(0, 0.65); + p70th = histogram->Quantile(0, 0.70); + p75th = histogram->Quantile(0, 0.75); + p80th = histogram->Quantile(0, 0.80); + p85th = histogram->Quantile(0, 0.85); + p90th = histogram->Quantile(0, 0.90); + p95th = histogram->Quantile(0, 0.95); + } double Log2=log(2); - for (int i = 0; i < (int)(histogram->GetSize(0)); ++i) + double mode_bin; + double mode_value = 0; + double variance = 0; + if (histogramIsCalculated) { - index[0] = i; - double prob = histogram->GetFrequency(index); + for (int i = 0; i < (int)(histogram->GetSize(0)); ++i) + { + index[0] = i; + double prob = histogram->GetFrequency(index); - if (prob < 0.1) - continue; - double voxelValue = histogram->GetBinMin(0, i) +binWidth * 0.5; + if (prob < 0.00000001) + continue; - sum_prob += prob; - squared_sum += prob * voxelValue*voxelValue; + voxelValue = histogram->GetBinMin(0, i) + binWidth * 0.5; - prob /= count; - mean_absolut_deviation += prob* std::abs(voxelValue - mean); + if (prob > mode_value) + { + mode_value = prob; + mode_bin = voxelValue; + } - kurtosis +=prob* (voxelValue - mean) * (voxelValue - mean) * (voxelValue - mean) * (voxelValue - mean); - skewness += prob* (voxelValue - mean) * (voxelValue - mean) * (voxelValue - mean); + sum_prob += prob; + squared_sum += prob * voxelValue*voxelValue; - uniformity += prob*prob; - if (prob > 0) - { - entropy += prob * std::log(prob) / Log2; + prob /= count; + mean_absolut_deviation += prob* std::abs(voxelValue - mean); + median_absolut_deviation += prob* std::abs(voxelValue - median); + variance += prob * (voxelValue - mean) * (voxelValue - mean); + + kurtosis += prob* (voxelValue - mean) * (voxelValue - mean) * (voxelValue - mean) * (voxelValue - mean); + skewness += prob* (voxelValue - mean) * (voxelValue - mean) * (voxelValue - mean); + + uniformity += prob*prob; + if (prob > 0) + { + entropy += prob * std::log(prob) / Log2; + } } } + entropy = -entropy; + double uncorrected_std_dev = std::sqrt(variance); double rms = std::sqrt(squared_sum / count); - kurtosis = kurtosis / (uncorrected_std_dev*uncorrected_std_dev * uncorrected_std_dev*uncorrected_std_dev); - skewness = skewness / (uncorrected_std_dev*uncorrected_std_dev * uncorrected_std_dev); - //mean_absolut_deviation = mean_absolut_deviation; + kurtosis = kurtosis / (variance * variance); + skewness = skewness / (variance * uncorrected_std_dev); double coveredGrayValueRange = range / imageRange; + double coefficient_of_variation = (mean == 0) ? 0 : std::sqrt(variance) / mean; + double quantile_coefficient_of_dispersion = (p75th - p25th) / (p75th + p25th); + + //Calculate the robust mean absolute deviation + //First, set all frequencies to 0 that are <10th or >90th percentile + double meanRobust = 0.0; + double robustMeanAbsoluteDeviation = 0.0; + if (histogramIsCalculated) + { + for (int i = 0; i < (int)(histogram->GetSize(0)); ++i) + { + index[0] = i; + if (histogram->GetBinMax(0, i) < p10th) + { + histogram->SetFrequencyOfIndex(index, 0); + } + else if (histogram->GetBinMin(0, i) > p90th) + { + histogram->SetFrequencyOfIndex(index, 0); + } + } - featureList.push_back(std::make_pair("FirstOrder Range",range)); - featureList.push_back(std::make_pair("FirstOrder Uniformity",uniformity)); - featureList.push_back(std::make_pair("FirstOrder Entropy",entropy)); - featureList.push_back(std::make_pair("FirstOrder Energy",squared_sum)); - featureList.push_back(std::make_pair("FirstOrder RMS",rms)); - featureList.push_back(std::make_pair("FirstOrder Kurtosis",kurtosis)); - featureList.push_back(std::make_pair("FirstOrder Skewness",skewness)); - featureList.push_back(std::make_pair("FirstOrder Mean absolute deviation",mean_absolut_deviation)); - featureList.push_back(std::make_pair("FirstOrder Covered Image Intensity Range",coveredGrayValueRange)); - - featureList.push_back(std::make_pair("FirstOrder Minimum",labelStatisticsImageFilter->GetMinimum(1))); - featureList.push_back(std::make_pair("FirstOrder Maximum",labelStatisticsImageFilter->GetMaximum(1))); - featureList.push_back(std::make_pair("FirstOrder Mean",labelStatisticsImageFilter->GetMean(1))); - featureList.push_back(std::make_pair("FirstOrder Variance",labelStatisticsImageFilter->GetVariance(1))); - featureList.push_back(std::make_pair("FirstOrder Sum",labelStatisticsImageFilter->GetSum(1))); - featureList.push_back(std::make_pair("FirstOrder Median",labelStatisticsImageFilter->GetMedian(1))); - featureList.push_back(std::make_pair("FirstOrder Standard deviation",labelStatisticsImageFilter->GetSigma(1))); - featureList.push_back(std::make_pair("FirstOrder No. of Voxel",labelStatisticsImageFilter->GetCount(1))); + //Calculate the mean + for (int i = 0; i < (int)(histogram->GetSize(0)); ++i) + { + index[0] = i; + meanRobust += histogram->GetFrequency(index) * 0.5 * (histogram->GetBinMin(0, i) + histogram->GetBinMax(0, i)); + } + meanRobust = meanRobust / histogram->GetTotalFrequency(); + for (int i = 0; i < (int)(histogram->GetSize(0)); ++i) + { + index[0] = i; + robustMeanAbsoluteDeviation += std::abs(histogram->GetFrequency(index) * + ((0.5 * (histogram->GetBinMin(0, i) + histogram->GetBinMax(0, i))) + - meanRobust + )); + } + robustMeanAbsoluteDeviation = robustMeanAbsoluteDeviation / histogram->GetTotalFrequency(); + } + + featureList.push_back(std::make_pair(params.prefix + "Mean", labelStatisticsImageFilter->GetMean(1))); + featureList.push_back(std::make_pair(params.prefix + "Unbiased Variance", labelStatisticsImageFilter->GetVariance(1))); //Siehe Definition von Unbiased Variance estimation. (Wird nicht durch n sondern durch n-1 normalisiert) + featureList.push_back(std::make_pair(params.prefix + "Biased Variance", variance)); + featureList.push_back(std::make_pair(params.prefix + "Skewness", skewness)); + featureList.push_back(std::make_pair(params.prefix + "Kurtosis", kurtosis)); + featureList.push_back(std::make_pair(params.prefix + "Median", labelStatisticsImageFilter->GetMedian(1))); + featureList.push_back(std::make_pair(params.prefix + "Minimum", labelStatisticsImageFilter->GetMinimum(1))); + featureList.push_back(std::make_pair(params.prefix + "Maximum", labelStatisticsImageFilter->GetMaximum(1))); + featureList.push_back(std::make_pair(params.prefix + "Range", range)); + featureList.push_back(std::make_pair(params.prefix + "Mean Absolute Deviation", mean_absolut_deviation)); + featureList.push_back(std::make_pair(params.prefix + "Robust Mean Absolute Deviation", robustMeanAbsoluteDeviation)); + featureList.push_back(std::make_pair(params.prefix + "Median Absolute Deviation", median_absolut_deviation)); + featureList.push_back(std::make_pair(params.prefix + "Coefficient Of Variation", coefficient_of_variation)); + featureList.push_back(std::make_pair(params.prefix + "Quantile Coefficient Of Dispersion", quantile_coefficient_of_dispersion)); + featureList.push_back(std::make_pair(params.prefix + "Energy", squared_sum)); + featureList.push_back(std::make_pair(params.prefix + "Root Mean Square", rms)); + + typename HistogramType::MeasurementVectorType mv(1); + mv[0] = 0; + typename HistogramType::IndexType resultingIndex; + histogram->GetIndex(mv, resultingIndex); + featureList.push_back(std::make_pair(params.prefix + "Robust Mean", meanRobust)); + featureList.push_back(std::make_pair(params.prefix + "Uniformity", uniformity)); + featureList.push_back(std::make_pair(params.prefix + "Entropy", entropy)); + featureList.push_back(std::make_pair(params.prefix + "Excess Kurtosis", kurtosis - 3)); + featureList.push_back(std::make_pair(params.prefix + "Covered Image Intensity Range", coveredGrayValueRange)); + featureList.push_back(std::make_pair(params.prefix + "Sum", labelStatisticsImageFilter->GetSum(1))); + featureList.push_back(std::make_pair(params.prefix + "Mode", mode_bin)); + featureList.push_back(std::make_pair(params.prefix + "Mode Probability", mode_value)); + featureList.push_back(std::make_pair(params.prefix + "Unbiased Standard deviation", labelStatisticsImageFilter->GetSigma(1))); + featureList.push_back(std::make_pair(params.prefix + "Biased Standard deviation", sqrt(variance))); + featureList.push_back(std::make_pair(params.prefix + "Number Of Voxels", labelStatisticsImageFilter->GetCount(1))); + + featureList.push_back(std::make_pair(params.prefix + "05th Percentile", p05th)); + featureList.push_back(std::make_pair(params.prefix + "10th Percentile", p10th)); + featureList.push_back(std::make_pair(params.prefix + "15th Percentile", p15th)); + featureList.push_back(std::make_pair(params.prefix + "20th Percentile", p20th)); + featureList.push_back(std::make_pair(params.prefix + "25th Percentile", p25th)); + featureList.push_back(std::make_pair(params.prefix + "30th Percentile", p30th)); + featureList.push_back(std::make_pair(params.prefix + "35th Percentile", p35th)); + featureList.push_back(std::make_pair(params.prefix + "40th Percentile", p40th)); + featureList.push_back(std::make_pair(params.prefix + "45th Percentile", p45th)); + featureList.push_back(std::make_pair(params.prefix + "50th Percentile", p50th)); + featureList.push_back(std::make_pair(params.prefix + "55th Percentile", p55th)); + featureList.push_back(std::make_pair(params.prefix + "60th Percentile", p60th)); + featureList.push_back(std::make_pair(params.prefix + "65th Percentile", p65th)); + featureList.push_back(std::make_pair(params.prefix + "70th Percentile", p70th)); + featureList.push_back(std::make_pair(params.prefix + "75th Percentile", p75th)); + featureList.push_back(std::make_pair(params.prefix + "80th Percentile", p80th)); + featureList.push_back(std::make_pair(params.prefix + "85th Percentile", p85th)); + featureList.push_back(std::make_pair(params.prefix + "90th Percentile", p90th)); + featureList.push_back(std::make_pair(params.prefix + "95th Percentile", p95th)); + featureList.push_back(std::make_pair(params.prefix + "Interquartile Range", (p75th - p25th))); + featureList.push_back(std::make_pair(params.prefix + "Image Dimension", VImageDimension)); + featureList.push_back(std::make_pair(params.prefix + "Voxel Space", voxelSpace)); + featureList.push_back(std::make_pair(params.prefix + "Voxel Volume", voxelVolume)); } -mitk::GIFFirstOrderStatistics::GIFFirstOrderStatistics() : - m_HistogramSize(256), m_UseCtRange(false) +mitk::GIFFirstOrderStatistics::GIFFirstOrderStatistics() { + SetShortName("fo"); + SetLongName("first-order"); + SetFeatureClassName("First Order"); } mitk::GIFFirstOrderStatistics::FeatureListType mitk::GIFFirstOrderStatistics::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) { + InitializeQuantifier(image, mask); FeatureListType featureList; ParameterStruct params; - params.m_HistogramSize = this->m_HistogramSize; - params.m_UseCtRange = this->m_UseCtRange; + params.MinimumIntensity = GetQuantifier()->GetMinimum(); + params.MaximumIntensity = GetQuantifier()->GetMaximum(); + params.Bins = GetQuantifier()->GetBins(); + params.prefix = FeatureDescriptionPrefix(); AccessByItk_3(image, CalculateFirstOrderStatistics, mask, featureList, params); return featureList; } mitk::GIFFirstOrderStatistics::FeatureNameListType mitk::GIFFirstOrderStatistics::GetFeatureNames() { FeatureNameListType featureList; - featureList.push_back("FirstOrder Minimum"); - featureList.push_back("FirstOrder Maximum"); - featureList.push_back("FirstOrder Mean"); - featureList.push_back("FirstOrder Variance"); - featureList.push_back("FirstOrder Sum"); - featureList.push_back("FirstOrder Median"); - featureList.push_back("FirstOrder Standard deviation"); - featureList.push_back("FirstOrder No. of Voxel"); + featureList.push_back("First Order::Minimum"); + featureList.push_back("First Order::Maximum"); + featureList.push_back("First Order::Mean"); + featureList.push_back("First Order::Variance"); + featureList.push_back("First Order::Sum"); + featureList.push_back("First Order::Median"); + featureList.push_back("First Order::Standard deviation"); + featureList.push_back("First Order::No. of Voxel"); return featureList; +} + + +void mitk::GIFFirstOrderStatistics::AddArguments(mitkCommandLineParser &parser) +{ + std::string name = GetOptionPrefix(); + + parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Volume-Statistic", "calculates volume based features", us::Any()); + AddQuantifierArguments(parser); +} + +void +mitk::GIFFirstOrderStatistics::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &, const Image::Pointer &maskNoNAN, FeatureListType &featureList) +{ + auto parsedArgs = GetParameter(); + if (parsedArgs.count(GetLongName())) + { + InitializeQuantifierFromParameters(feature, maskNoNAN); + MITK_INFO << "Start calculating first order features ...."; + auto localResults = this->CalculateFeatures(feature, maskNoNAN); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating first order features...."; + } +} + +std::string mitk::GIFFirstOrderStatistics::GetCurrentFeatureEncoding() +{ + return QuantifierParameterString(); } \ No newline at end of file diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFGrayLevelRunLength.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFGrayLevelRunLength.cpp deleted file mode 100644 index 5591176612..0000000000 --- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFGrayLevelRunLength.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/*=================================================================== - -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 - -// MITK -#include -#include -#include - -// ITK -#include -#include - -// STL -#include - -template -void - CalculateGrayLevelRunLengthFeatures(itk::Image* itkImage, mitk::Image::Pointer mask, mitk::GIFGrayLevelRunLength::FeatureListType & featureList, mitk::GIFGrayLevelRunLength::ParameterStruct params) -{ - typedef itk::Image ImageType; - typedef itk::Image MaskType; - typedef itk::Statistics::EnhancedScalarImageToRunLengthFeaturesFilter FilterType; - typedef itk::MinimumMaximumImageCalculator MinMaxComputerType; - typedef typename FilterType::RunLengthFeaturesFilterType TextureFilterType; - - typename MaskType::Pointer maskImage = MaskType::New(); - mitk::CastToItkImage(mask, maskImage); - - typename FilterType::Pointer filter = FilterType::New(); - - typename FilterType::OffsetVector::Pointer newOffset = FilterType::OffsetVector::New(); - auto oldOffsets = filter->GetOffsets(); - auto oldOffsetsIterator = oldOffsets->Begin(); - while (oldOffsetsIterator != oldOffsets->End()) - { - bool continueOuterLoop = false; - typename FilterType::OffsetType offset = oldOffsetsIterator->Value(); - for (unsigned int i = 0; i < VImageDimension; ++i) - { - if (params.m_Direction == i + 2 && offset[i] != 0) - { - continueOuterLoop = true; - } - } - if (params.m_Direction == 1) - { - offset[0] = 0; - offset[1] = 0; - offset[2] = 1; - newOffset->push_back(offset); - break; - } - - oldOffsetsIterator++; - if (continueOuterLoop) - continue; - newOffset->push_back(offset); - } - filter->SetOffsets(newOffset); - - - // All features are required - typename FilterType::FeatureNameVectorPointer requestedFeatures = FilterType::FeatureNameVector::New(); - requestedFeatures->push_back(TextureFilterType::ShortRunEmphasis); - requestedFeatures->push_back(TextureFilterType::LongRunEmphasis); - requestedFeatures->push_back(TextureFilterType::GreyLevelNonuniformity); - requestedFeatures->push_back(TextureFilterType::RunLengthNonuniformity); - requestedFeatures->push_back(TextureFilterType::LowGreyLevelRunEmphasis); - requestedFeatures->push_back(TextureFilterType::HighGreyLevelRunEmphasis); - requestedFeatures->push_back(TextureFilterType::ShortRunLowGreyLevelEmphasis); - requestedFeatures->push_back(TextureFilterType::ShortRunHighGreyLevelEmphasis); - requestedFeatures->push_back(TextureFilterType::LongRunLowGreyLevelEmphasis); - requestedFeatures->push_back(TextureFilterType::LongRunHighGreyLevelEmphasis); - requestedFeatures->push_back(TextureFilterType::RunPercentage); - requestedFeatures->push_back(TextureFilterType::NumberOfRuns); - - typename MinMaxComputerType::Pointer minMaxComputer = MinMaxComputerType::New(); - minMaxComputer->SetImage(itkImage); - minMaxComputer->Compute(); - - filter->SetInput(itkImage); - filter->SetMaskImage(maskImage); - filter->SetRequestedFeatures(requestedFeatures); - int rangeOfPixels = params.m_Range; - if (rangeOfPixels < 2) - rangeOfPixels = 256; - - if (params.m_UseCtRange) - { - filter->SetPixelValueMinMax((TPixel)(-1024.5),(TPixel)(3096.5)); - filter->SetNumberOfBinsPerAxis(3096.5+1024.5); - } else - { - filter->SetPixelValueMinMax(minMaxComputer->GetMinimum(),minMaxComputer->GetMaximum()); - filter->SetNumberOfBinsPerAxis(rangeOfPixels); - } - - filter->SetDistanceValueMinMax(0,rangeOfPixels); - - filter->Update(); - - auto featureMeans = filter->GetFeatureMeans (); - auto featureStd = filter->GetFeatureStandardDeviations(); - - std::ostringstream ss; - ss << rangeOfPixels; - std::string strRange = ss.str(); - for (std::size_t i = 0; i < featureMeans->size(); ++i) - { - switch (i) - { - case TextureFilterType::ShortRunEmphasis : - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") ShortRunEmphasis Means",featureMeans->ElementAt(i))); - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") ShortRunEmphasis Std.",featureStd->ElementAt(i))); - break; - case TextureFilterType::LongRunEmphasis : - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") LongRunEmphasis Means",featureMeans->ElementAt(i))); - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") LongRunEmphasis Std.",featureStd->ElementAt(i))); - break; - case TextureFilterType::GreyLevelNonuniformity : - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") GreyLevelNonuniformity Means",featureMeans->ElementAt(i))); - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") GreyLevelNonuniformity Std.",featureStd->ElementAt(i))); - break; - case TextureFilterType::RunLengthNonuniformity : - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") RunLengthNonuniformity Means",featureMeans->ElementAt(i))); - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") RunLengthNonuniformity Std.",featureStd->ElementAt(i))); - break; - case TextureFilterType::LowGreyLevelRunEmphasis : - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") LowGreyLevelRunEmphasis Means",featureMeans->ElementAt(i))); - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") LowGreyLevelRunEmphasis Std.",featureStd->ElementAt(i))); - break; - case TextureFilterType::HighGreyLevelRunEmphasis : - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") HighGreyLevelRunEmphasis Means",featureMeans->ElementAt(i))); - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") HighGreyLevelRunEmphasis Std.",featureStd->ElementAt(i))); - break; - case TextureFilterType::ShortRunLowGreyLevelEmphasis : - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") ShortRunLowGreyLevelEmphasis Means",featureMeans->ElementAt(i))); - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") ShortRunLowGreyLevelEmphasis Std.",featureStd->ElementAt(i))); - break; - case TextureFilterType::ShortRunHighGreyLevelEmphasis : - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") ShortRunHighGreyLevelEmphasis Means",featureMeans->ElementAt(i))); - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") ShortRunHighGreyLevelEmphasis Std.",featureStd->ElementAt(i))); - break; - case TextureFilterType::LongRunLowGreyLevelEmphasis : - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") LongRunLowGreyLevelEmphasis Means",featureMeans->ElementAt(i))); - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") LongRunLowGreyLevelEmphasis Std.",featureStd->ElementAt(i))); - break; - case TextureFilterType::LongRunHighGreyLevelEmphasis : - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") LongRunHighGreyLevelEmphasis Means",featureMeans->ElementAt(i))); - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") LongRunHighGreyLevelEmphasis Std.",featureStd->ElementAt(i))); - break; - case TextureFilterType::RunPercentage : - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") RunPercentage Means",featureMeans->ElementAt(i))); - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") RunPercentage Std.",featureStd->ElementAt(i))); - break; - case TextureFilterType::NumberOfRuns : - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") NumberOfRuns Means",featureMeans->ElementAt(i))); - featureList.push_back(std::make_pair("RunLength. ("+ strRange+") NumberOfRuns Std.",featureStd->ElementAt(i))); - break; - default: - break; - } - } -} - -mitk::GIFGrayLevelRunLength::GIFGrayLevelRunLength(): -m_Range(1.0), m_UseCtRange(false), m_Direction(0) -{ -} - -mitk::GIFGrayLevelRunLength::FeatureListType mitk::GIFGrayLevelRunLength::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) -{ - FeatureListType featureList; - - ParameterStruct params; - params.m_UseCtRange=m_UseCtRange; - params.m_Range = m_Range; - params.m_Direction = m_Direction; - - AccessByItk_3(image, CalculateGrayLevelRunLengthFeatures, mask, featureList,params); - - return featureList; -} - -mitk::GIFGrayLevelRunLength::FeatureNameListType mitk::GIFGrayLevelRunLength::GetFeatureNames() -{ - FeatureNameListType featureList; - featureList.push_back("RunLength. ShortRunEmphasis Means"); - featureList.push_back("RunLength. ShortRunEmphasis Std."); - featureList.push_back("RunLength. LongRunEmphasis Means"); - featureList.push_back("RunLength. LongRunEmphasis Std."); - featureList.push_back("RunLength. GreyLevelNonuniformity Means"); - featureList.push_back("RunLength. GreyLevelNonuniformity Std."); - featureList.push_back("RunLength. RunLengthNonuniformity Means"); - featureList.push_back("RunLength. RunLengthNonuniformity Std."); - featureList.push_back("RunLength. LowGreyLevelRunEmphasis Means"); - featureList.push_back("RunLength. LowGreyLevelRunEmphasis Std."); - featureList.push_back("RunLength. HighGreyLevelRunEmphasis Means"); - featureList.push_back("RunLength. HighGreyLevelRunEmphasis Std."); - featureList.push_back("RunLength. ShortRunLowGreyLevelEmphasis Means"); - featureList.push_back("RunLength. ShortRunLowGreyLevelEmphasis Std."); - featureList.push_back("RunLength. ShortRunHighGreyLevelEmphasis Means"); - featureList.push_back("RunLength. ShortRunHighGreyLevelEmphasis Std."); - featureList.push_back("RunLength. LongRunHighGreyLevelEmphasis Means"); - featureList.push_back("RunLength. LongRunHighGreyLevelEmphasis Std."); - return featureList; -} \ No newline at end of file diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFGreyLevelDistanceZone.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFGreyLevelDistanceZone.cpp new file mode 100644 index 0000000000..74e875ab26 --- /dev/null +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFGreyLevelDistanceZone.cpp @@ -0,0 +1,470 @@ +#include + +// MITK +#include +#include +#include +#include +#include + +// ITK +#include +#include +#include +#include +#include + +namespace mitk{ + struct GreyLevelDistanceZoneMatrixHolder + { + public: + GreyLevelDistanceZoneMatrixHolder(mitk::IntensityQuantifier::Pointer quantifier, int number, int maxSize); + + int IntensityToIndex(double intensity); + + int m_NumberOfBins; + int m_MaximumSize; + int m_NumerOfVoxels; + Eigen::MatrixXd m_Matrix; + mitk::IntensityQuantifier::Pointer m_Quantifier; + + }; +} + +static +void MatrixFeaturesTo(mitk::GreyLevelDistanceZoneFeatures features, + std::string prefix, + mitk::GIFGreyLevelDistanceZone::FeatureListType &featureList); + + + +mitk::GreyLevelDistanceZoneMatrixHolder::GreyLevelDistanceZoneMatrixHolder(mitk::IntensityQuantifier::Pointer quantifier, int number, int maxSize) : + m_NumberOfBins(number), + m_MaximumSize(maxSize), + m_NumerOfVoxels(0), + m_Quantifier(quantifier) +{ + m_Matrix.resize(number, maxSize); + m_Matrix.fill(0); +} + +int mitk::GreyLevelDistanceZoneMatrixHolder::IntensityToIndex(double intensity) +{ + return m_Quantifier->IntensityToIndex(intensity); +} + + +template +int +CalculateGlSZMatrix(itk::Image* itkImage, + itk::Image* mask, + itk::Image* distanceImage, + std::vector > offsets, + bool estimateLargestRegion, + mitk::GreyLevelDistanceZoneMatrixHolder &holder) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskImageType; + typedef typename ImageType::IndexType IndexType; + + typedef itk::ImageRegionIteratorWithIndex ConstIterType; + typedef itk::ImageRegionIteratorWithIndex ConstMaskIterType; + + auto region = mask->GetLargestPossibleRegion(); + typename MaskImageType::RegionType newRegion; + newRegion.SetSize(region.GetSize()); + newRegion.SetIndex(region.GetIndex()); + + ConstIterType imageIter(itkImage, itkImage->GetLargestPossibleRegion()); + ConstMaskIterType maskIter(mask, mask->GetLargestPossibleRegion()); + + typename MaskImageType::Pointer visitedImage = MaskImageType::New(); + visitedImage->SetRegions(newRegion); + visitedImage->Allocate(); + visitedImage->FillBuffer(0); + + int largestRegion = 0; + holder.m_NumberOfBins = 0; + + while (!maskIter.IsAtEnd()) + { + if (maskIter.Value() > 0 ) + { + auto startIntensityIndex = holder.IntensityToIndex(imageIter.Value()); + std::vector indices; + indices.push_back(maskIter.GetIndex()); + unsigned int steps = 0; + int smallestDistance = 500; + + while (indices.size() > 0) + { + auto currentIndex = indices.back(); + indices.pop_back(); + + if (!region.IsInside(currentIndex)) + { + continue; + } + + auto wasVisited = visitedImage->GetPixel(currentIndex); + auto newIntensityIndex = holder.IntensityToIndex(itkImage->GetPixel(currentIndex)); + auto isInMask = mask->GetPixel(currentIndex); + + if ((isInMask > 0) && + (newIntensityIndex == startIntensityIndex) && + (wasVisited < 1)) + { + ++(holder.m_NumerOfVoxels); + smallestDistance = (smallestDistance > distanceImage->GetPixel(currentIndex)) ? distanceImage->GetPixel(currentIndex) : smallestDistance; + ++steps; + visitedImage->SetPixel(currentIndex, 1); + for (auto offset : offsets) + { + auto newIndex = currentIndex + offset; + indices.push_back(newIndex); + newIndex = currentIndex - offset; + indices.push_back(newIndex); + } + + } + } + if (steps > 0) + { + largestRegion = std::max(steps, largestRegion); + steps = std::min(steps, holder.m_MaximumSize); + if (!estimateLargestRegion) + { + holder.m_Matrix(startIntensityIndex, smallestDistance-1) += 1; + } + } + } + ++imageIter; + ++maskIter; + } + return largestRegion; +} + +template +void itkErode2( + itk::Image *sourceImage, + mitk::Image::Pointer &resultImage, + int &maxDistance) +{ + typedef itk::Image ImageType; + typedef unsigned short MaskType; + typedef itk::Image MaskImageType; + + typename MaskImageType::Pointer distanceImage = MaskImageType::New(); + distanceImage->SetRegions(sourceImage->GetLargestPossibleRegion()); + distanceImage->SetOrigin(sourceImage->GetOrigin()); + distanceImage->SetSpacing(sourceImage->GetSpacing()); + distanceImage->SetDirection(sourceImage->GetDirection()); + distanceImage->Allocate(); + distanceImage->FillBuffer(std::numeric_limits::max()-1); + + typename ImageType::SizeType radius; + radius.Fill(1); + itk::NeighborhoodIterator neighbourIter(radius, sourceImage, sourceImage->GetLargestPossibleRegion()); + itk::NeighborhoodIterator distanceIter(radius, distanceImage, distanceImage->GetLargestPossibleRegion()); + + bool imageChanged = true; + while (imageChanged) + { + imageChanged = false; + maxDistance = 0; + neighbourIter.GoToBegin(); + distanceIter.GoToBegin(); + while (!neighbourIter.IsAtEnd()) + { + MaskType oldDistance = distanceIter.GetCenterPixel(); + maxDistance = std::max(maxDistance, oldDistance); + if (neighbourIter.GetCenterPixel() < 1) + { + if (oldDistance > 0) + { + distanceIter.SetCenterPixel(0); + imageChanged = true; + } + } + else if (oldDistance>0) { + MaskType minimumDistance = oldDistance; + for (unsigned int i = 0; i < distanceIter.Size(); ++i) + { + minimumDistance = std::min(minimumDistance, 1+distanceIter.GetPixel(i)); + } + if (minimumDistance != oldDistance) + { + distanceIter.SetCenterPixel(minimumDistance); + imageChanged = true; + } + } + + ++neighbourIter; + ++distanceIter; + } + } + + mitk::CastToMitkImage(distanceImage, resultImage); +} + +void erode(mitk::Image::Pointer input, mitk::Image::Pointer &output, int &maxDistance) +{ + AccessByItk_2(input, itkErode2, output, maxDistance); +} + + +void erodeAndAdd(mitk::Image::Pointer input, mitk::Image::Pointer& finalOutput, int &maxDistance) +{ + maxDistance = 0; + erode(input, finalOutput, maxDistance); +} + + +void static CalculateFeatures( + mitk::GreyLevelDistanceZoneMatrixHolder &holder, + mitk::GreyLevelDistanceZoneFeatures & results + ) +{ + auto SgzMatrix = holder.m_Matrix; + auto pgzMatrix = holder.m_Matrix; + auto pgMatrix = holder.m_Matrix; + auto pzMatrix = holder.m_Matrix; + + double Ns = pgzMatrix.sum(); + pgzMatrix /= Ns; + pgMatrix.rowwise().normalize(); + pzMatrix.colwise().normalize(); + + for (int i = 0; i < pgzMatrix.rows(); ++i) + for (int j = 0; j < pgzMatrix.cols(); ++j) + { + if (pgzMatrix(i, j) != pgzMatrix(i, j)) + pgzMatrix(i, j) = 0; + if (pgMatrix(i, j) != pgMatrix(i, j)) + pgMatrix(i, j) = 0; + if (pzMatrix(i, j) != pzMatrix(i, j)) + pzMatrix(i, j) = 0; + } + + Eigen::VectorXd SgVector = SgzMatrix.rowwise().sum(); + Eigen::VectorXd SzVector = SgzMatrix.colwise().sum(); + + for (int j = 0; j < SzVector.size(); ++j) + { + results.SmallDistanceEmphasis += SzVector(j) / (j+1) / (j+1); + results.LargeDistanceEmphasis += SzVector(j) * (j + 1.0) * (j + 1.0); + results.ZoneDistanceNonUniformity += SzVector(j) * SzVector(j); + results.ZoneDistanceNoneUniformityNormalized += SzVector(j) * SzVector(j); + } + for (int i = 0; i < SgVector.size(); ++i) + { + results.LowGreyLevelEmphasis += SgVector(i) / (i + 1) / (i + 1); + results.HighGreyLevelEmphasis += SgVector(i) * (i + 1) * (i + 1); + results.GreyLevelNonUniformity += SgVector(i)*SgVector(i); + results.GreyLevelNonUniformityNormalized += SgVector(i)*SgVector(i); + } + + for (int i = 0; i < SgzMatrix.rows(); ++i) + { + for (int j = 0; j < SgzMatrix.cols(); ++j) + { + results.SmallDistanceLowGreyLevelEmphasis += SgzMatrix(i, j) / (i + 1) / (i + 1) / (j + 1) / (j + 1); + results.SmallDistanceHighGreyLevelEmphasis += SgzMatrix(i, j) * (i + 1) * (i + 1) / (j + 1) / (j + 1); + results.LargeDistanceLowGreyLevelEmphasis += SgzMatrix(i, j) / (i + 1) / (i + 1) * (j + 1.0) * (j + 1.0); + results.LargeDistanceHighGreyLevelEmphasis += SgzMatrix(i, j) * (i + 1) * (i + 1) * (j + 1.0) * (j + 1.0); + results.ZonePercentage += SgzMatrix(i, j); + + results.GreyLevelMean += (i + 1)*pgzMatrix(i, j); + results.ZoneDistanceMean += (j + 1)*pgzMatrix(i, j); + if (pgzMatrix(i, j) > 0) + results.ZoneDistanceEntropy -= pgzMatrix(i, j) * std::log(pgzMatrix(i, j)) / std::log(2); + } + } + + for (int i = 0; i < SgzMatrix.rows(); ++i) + { + for (int j = 0; j < SgzMatrix.cols(); ++j) + { + results.GreyLevelVariance += (i + 1 - results.GreyLevelMean)*(i + 1 - results.GreyLevelMean)*pgzMatrix(i, j); + results.ZoneDistanceVariance += (j + 1 - results.ZoneDistanceMean)*(j + 1 - results.ZoneDistanceMean)*pgzMatrix(i, j); + } + } + + results.SmallDistanceEmphasis /= Ns; + results.LargeDistanceEmphasis /= Ns; + results.LowGreyLevelEmphasis /= Ns; + results.HighGreyLevelEmphasis /= Ns; + + results.SmallDistanceLowGreyLevelEmphasis /= Ns; + results.SmallDistanceHighGreyLevelEmphasis /= Ns; + results.LargeDistanceLowGreyLevelEmphasis /= Ns; + results.LargeDistanceHighGreyLevelEmphasis /= Ns; + results.GreyLevelNonUniformity /= Ns; + results.GreyLevelNonUniformityNormalized /= Ns*Ns; + results.ZoneDistanceNonUniformity /= Ns; + results.ZoneDistanceNoneUniformityNormalized /= Ns*Ns; + + results.ZonePercentage = Ns / holder.m_NumerOfVoxels;// results.ZonePercentage; +} + +template +static void +CalculateGreyLevelDistanceZoneFeatures(itk::Image* itkImage, mitk::Image::Pointer mask, mitk::GIFGreyLevelDistanceZone::FeatureListType & featureList, mitk::GIFGreyLevelDistanceZone::GIFGreyLevelDistanceZoneConfiguration config) +{ + typedef itk::Image MaskType; + typedef itk::Neighborhood NeighborhoodType; + typedef itk::Offset OffsetType; + + /////////////////////////////////////////////////////////////////////////////////////////////// + int maximumDistance = 0; + mitk::Image::Pointer mitkDistanceImage = mitk::Image::New(); + erodeAndAdd(config.distanceMask, mitkDistanceImage, maximumDistance); + typename MaskType::Pointer distanceImage = MaskType::New(); + mitk::CastToItkImage(mitkDistanceImage, distanceImage); + + typename MaskType::Pointer maskImage = MaskType::New(); + mitk::CastToItkImage(mask, maskImage); + + //Find possible directions + std::vector < itk::Offset > offsetVector; + NeighborhoodType hood; + hood.SetRadius(1); + unsigned int centerIndex = hood.GetCenterNeighborhoodIndex(); + OffsetType offset; + for (unsigned int d = 0; d < centerIndex; d++) + { + offset = hood.GetOffset(d); + bool useOffset = true; + for (unsigned int i = 0; i < VImageDimension; ++i) + { + if ((config.direction == i + 2) && offset[i] != 0) + { + useOffset = false; + } + } + if (useOffset) + { + offsetVector.push_back(offset); + } + } + if (config.direction == 1) + { + offsetVector.clear(); + offset[0] = 0; + offset[1] = 0; + offset[2] = 1; + offsetVector.push_back(offset); + } + + MITK_INFO << "Maximum Distance: " << maximumDistance; + std::vector resultVector; + mitk::GreyLevelDistanceZoneMatrixHolder holderOverall(config.Quantifier, config.Bins, maximumDistance + 1); + mitk::GreyLevelDistanceZoneFeatures overallFeature; + CalculateGlSZMatrix(itkImage, maskImage, distanceImage, offsetVector, false, holderOverall); + CalculateFeatures(holderOverall, overallFeature); + + MatrixFeaturesTo(overallFeature, config.prefix, featureList); +} + + +static +void MatrixFeaturesTo(mitk::GreyLevelDistanceZoneFeatures features, + std::string prefix, + mitk::GIFGreyLevelDistanceZone::FeatureListType &featureList) +{ + featureList.push_back(std::make_pair(prefix + "Small Distance Emphasis", features.SmallDistanceEmphasis)); + featureList.push_back(std::make_pair(prefix + "Large Distance Emphasis", features.LargeDistanceEmphasis)); + featureList.push_back(std::make_pair(prefix + "Low Grey Level Emphasis", features.LowGreyLevelEmphasis)); + featureList.push_back(std::make_pair(prefix + "High Grey Level Emphasis", features.HighGreyLevelEmphasis)); + featureList.push_back(std::make_pair(prefix + "Small Distance Low Grey Level Emphasis", features.SmallDistanceLowGreyLevelEmphasis)); + featureList.push_back(std::make_pair(prefix + "Small Distance High Grey Level Emphasis", features.SmallDistanceHighGreyLevelEmphasis)); + featureList.push_back(std::make_pair(prefix + "Large Distance Low Grey Level Emphasis", features.LargeDistanceLowGreyLevelEmphasis)); + featureList.push_back(std::make_pair(prefix + "Large Distance High Grey Level Emphasis", features.LargeDistanceHighGreyLevelEmphasis)); + featureList.push_back(std::make_pair(prefix + "Grey Level Non-Uniformity", features.GreyLevelNonUniformity)); + featureList.push_back(std::make_pair(prefix + "Grey Level Non-Uniformity Normalized", features.GreyLevelNonUniformityNormalized)); + featureList.push_back(std::make_pair(prefix + "Distance Size Non-Uniformity", features.ZoneDistanceNonUniformity)); + featureList.push_back(std::make_pair(prefix + "Distance Size Non-Uniformity Normalized", features.ZoneDistanceNoneUniformityNormalized)); + featureList.push_back(std::make_pair(prefix + "Zone Percentage", features.ZonePercentage)); + featureList.push_back(std::make_pair(prefix + "Grey Level Mean", features.GreyLevelMean)); + featureList.push_back(std::make_pair(prefix + "Grey Level Variance", features.GreyLevelVariance)); + featureList.push_back(std::make_pair(prefix + "Zone Distance Mean", features.ZoneDistanceMean)); + featureList.push_back(std::make_pair(prefix + "Zone Distance Variance", features.ZoneDistanceVariance)); + featureList.push_back(std::make_pair(prefix + "Zone Distance Entropy", features.ZoneDistanceEntropy)); + featureList.push_back(std::make_pair(prefix + "Grey Level Entropy", features.ZoneDistanceEntropy)); +} + + mitk::GIFGreyLevelDistanceZone::GIFGreyLevelDistanceZone() +{ + SetShortName("gldz"); + SetLongName("distance-zone"); + SetFeatureClassName("Grey Level Distance Zone"); +} + +mitk::GIFGreyLevelDistanceZone::FeatureListType mitk::GIFGreyLevelDistanceZone::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) +{ + InitializeQuantifier(image, mask); + FeatureListType featureList; + + GIFGreyLevelDistanceZoneConfiguration config; + config.direction = GetDirection(); + + if (GetMorphMask().IsNull()) + { + config.distanceMask = mask->Clone(); + } + else + { + config.distanceMask = GetMorphMask(); + } + + config.MinimumIntensity = GetQuantifier()->GetMinimum(); + config.MaximumIntensity = GetQuantifier()->GetMaximum(); + config.Bins = GetQuantifier()->GetBins(); + config.prefix = FeatureDescriptionPrefix(); + config.Quantifier = GetQuantifier(); + + AccessByItk_3(image, CalculateGreyLevelDistanceZoneFeatures, mask, featureList, config); + + return featureList; +} + +mitk::GIFGreyLevelDistanceZone::FeatureNameListType mitk::GIFGreyLevelDistanceZone::GetFeatureNames() +{ + FeatureNameListType featureList; + return featureList; +} + + + + +void mitk::GIFGreyLevelDistanceZone::AddArguments(mitkCommandLineParser &parser) +{ + std::string name = GetOptionPrefix(); + + parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Grey Level Distance Zone", "Calculates the size zone based features.", us::Any()); + AddQuantifierArguments(parser); +} + +void +mitk::GIFGreyLevelDistanceZone::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList) +{ + auto parsedArgs = GetParameter(); + std::string name = GetOptionPrefix(); + + if (parsedArgs.count(GetLongName())) + { + InitializeQuantifierFromParameters(feature, mask); + + MITK_INFO << "Start calculating Grey Level Distance Zone ...."; + auto localResults = this->CalculateFeatures(feature, maskNoNAN); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating Grey Level Distance Zone."; + } + +} + +std::string mitk::GIFGreyLevelDistanceZone::GetCurrentFeatureEncoding() +{ + return QuantifierParameterString(); +} + diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFGreyLevelRunLength.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFGreyLevelRunLength.cpp new file mode 100644 index 0000000000..42ac98184f --- /dev/null +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFGreyLevelRunLength.cpp @@ -0,0 +1,289 @@ +/*=================================================================== + +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 + +// MITK +#include +#include +#include + +// ITK +#include + +// STL +#include + +template +void + CalculateGrayLevelRunLengthFeatures(itk::Image* itkImage, mitk::Image::Pointer mask, mitk::GIFGreyLevelRunLength::FeatureListType & featureList, mitk::GIFGreyLevelRunLength::ParameterStruct params) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskType; + typedef itk::Statistics::EnhancedScalarImageToRunLengthFeaturesFilter FilterType; + typedef typename FilterType::RunLengthFeaturesFilterType TextureFilterType; + + typename MaskType::Pointer maskImage = MaskType::New(); + mitk::CastToItkImage(mask, maskImage); + + typename FilterType::Pointer filter = FilterType::New(); + typename FilterType::Pointer filter2 = FilterType::New(); + + typename FilterType::OffsetVector::Pointer newOffset = FilterType::OffsetVector::New(); + auto oldOffsets = filter->GetOffsets(); + auto oldOffsetsIterator = oldOffsets->Begin(); + while (oldOffsetsIterator != oldOffsets->End()) + { + bool continueOuterLoop = false; + typename FilterType::OffsetType offset = oldOffsetsIterator->Value(); + for (unsigned int i = 0; i < VImageDimension; ++i) + { + if (params.m_Direction == i + 2 && offset[i] != 0) + { + continueOuterLoop = true; + } + } + if (params.m_Direction == 1) + { + offset[0] = 0; + offset[1] = 0; + offset[2] = 1; + newOffset->push_back(offset); + break; + } + + oldOffsetsIterator++; + if (continueOuterLoop) + continue; + newOffset->push_back(offset); + } + filter->SetOffsets(newOffset); + filter2->SetOffsets(newOffset); + + + // All features are required + typename FilterType::FeatureNameVectorPointer requestedFeatures = FilterType::FeatureNameVector::New(); + requestedFeatures->push_back(TextureFilterType::ShortRunEmphasis); + requestedFeatures->push_back(TextureFilterType::LongRunEmphasis); + requestedFeatures->push_back(TextureFilterType::GreyLevelNonuniformity); + requestedFeatures->push_back(TextureFilterType::GreyLevelNonuniformityNormalized); + requestedFeatures->push_back(TextureFilterType::RunLengthNonuniformity); + requestedFeatures->push_back(TextureFilterType::RunLengthNonuniformityNormalized); + requestedFeatures->push_back(TextureFilterType::LowGreyLevelRunEmphasis); + requestedFeatures->push_back(TextureFilterType::HighGreyLevelRunEmphasis); + requestedFeatures->push_back(TextureFilterType::ShortRunLowGreyLevelEmphasis); + requestedFeatures->push_back(TextureFilterType::ShortRunHighGreyLevelEmphasis); + requestedFeatures->push_back(TextureFilterType::LongRunLowGreyLevelEmphasis); + requestedFeatures->push_back(TextureFilterType::LongRunHighGreyLevelEmphasis); + requestedFeatures->push_back(TextureFilterType::RunPercentage); + requestedFeatures->push_back(TextureFilterType::NumberOfRuns); + requestedFeatures->push_back(TextureFilterType::GreyLevelVariance); + requestedFeatures->push_back(TextureFilterType::RunLengthVariance); + requestedFeatures->push_back(TextureFilterType::RunEntropy); + + filter->SetInput(itkImage); + filter->SetMaskImage(maskImage); + filter->SetRequestedFeatures(requestedFeatures); + filter2->SetInput(itkImage); + filter2->SetMaskImage(maskImage); + filter2->SetRequestedFeatures(requestedFeatures); + int numberOfBins = params.Bins; + if (numberOfBins < 2) + numberOfBins = 256; + + double minRange = params.MinimumIntensity; + double maxRange = params.MaximumIntensity; + + filter->SetPixelValueMinMax(minRange, maxRange); + filter->SetNumberOfBinsPerAxis(numberOfBins); + filter2->SetPixelValueMinMax(minRange, maxRange); + filter2->SetNumberOfBinsPerAxis(numberOfBins); + + filter->SetDistanceValueMinMax(0, numberOfBins); + filter2->SetDistanceValueMinMax(0, numberOfBins); + + filter2->CombinedFeatureCalculationOn(); + + filter->Update(); + filter2->Update(); + + auto featureMeans = filter->GetFeatureMeans (); + auto featureStd = filter->GetFeatureStandardDeviations(); + auto featureCombined = filter2->GetFeatureMeans(); + + for (std::size_t i = 0; i < featureMeans->size(); ++i) + { + switch (i) + { + case TextureFilterType::ShortRunEmphasis : + featureList.push_back(std::make_pair(params.featurePrefix + "Short run emphasis Means",featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Short run emphasis Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Short run emphasis Comb.", featureCombined->ElementAt(i))); + break; + case TextureFilterType::LongRunEmphasis : + featureList.push_back(std::make_pair(params.featurePrefix + "Long run emphasis Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Long run emphasis Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Long run emphasis Comb.", featureCombined->ElementAt(i))); + break; + case TextureFilterType::GreyLevelNonuniformity : + featureList.push_back(std::make_pair(params.featurePrefix + "Grey level nonuniformity Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Grey level nonuniformity Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Grey level nonuniformity Comb.", featureCombined->ElementAt(i))); + break; + case TextureFilterType::GreyLevelNonuniformityNormalized : + featureList.push_back(std::make_pair(params.featurePrefix + "Grey level nonuniformity normalized Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Grey level nonuniformity normalized Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Grey level nonuniformity normalized Comb.", featureCombined->ElementAt(i))); + break; + case TextureFilterType::RunLengthNonuniformity : + featureList.push_back(std::make_pair(params.featurePrefix + "Run length nonuniformity Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Run length nonuniformity Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Run length nonuniformity Comb.", featureCombined->ElementAt(i))); + break; + case TextureFilterType::RunLengthNonuniformityNormalized : + featureList.push_back(std::make_pair(params.featurePrefix + "Run length nonuniformity normalized Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Run length nonuniformity normalized Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Run length nonuniformity normalized Comb.", featureCombined->ElementAt(i))); + break; + case TextureFilterType::LowGreyLevelRunEmphasis : + featureList.push_back(std::make_pair(params.featurePrefix + "Low grey level run emphasis Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Low grey level run emphasis Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Low grey level run emphasis Comb.", featureCombined->ElementAt(i))); + break; + case TextureFilterType::HighGreyLevelRunEmphasis : + featureList.push_back(std::make_pair(params.featurePrefix + "High grey level run emphasis Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "High grey level run emphasis Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "High grey level run emphasis Comb.", featureCombined->ElementAt(i))); + break; + case TextureFilterType::ShortRunLowGreyLevelEmphasis : + featureList.push_back(std::make_pair(params.featurePrefix + "Short run low grey level emphasis Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Short run low grey level emphasis Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Short run low grey level emphasis Comb.", featureCombined->ElementAt(i))); + break; + case TextureFilterType::ShortRunHighGreyLevelEmphasis : + featureList.push_back(std::make_pair(params.featurePrefix + "Short run high grey level emphasis Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Short run high grey level emphasis Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Short run high grey level emphasis Comb.", featureCombined->ElementAt(i))); + break; + case TextureFilterType::LongRunLowGreyLevelEmphasis : + featureList.push_back(std::make_pair(params.featurePrefix + "Long run low grey level emphasis Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Long run low grey level emphasis Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Long run low grey level emphasis Comb.", featureCombined->ElementAt(i))); + break; + case TextureFilterType::LongRunHighGreyLevelEmphasis : + featureList.push_back(std::make_pair(params.featurePrefix + "Long run high grey level emphasis Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Long run high grey level emphasis Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Long run high grey level emphasis Comb.", featureCombined->ElementAt(i))); + break; + case TextureFilterType::RunPercentage : + featureList.push_back(std::make_pair(params.featurePrefix + "Run percentage Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Run percentage Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Run percentage Comb.", featureCombined->ElementAt(i) / newOffset->size())); + break; + case TextureFilterType::NumberOfRuns : + featureList.push_back(std::make_pair(params.featurePrefix + "Number of runs Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Number of runs Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Number of runs Comb.", featureCombined->ElementAt(i))); + break; + case TextureFilterType::GreyLevelVariance : + featureList.push_back(std::make_pair(params.featurePrefix + "Grey level variance Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Grey level variance Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Grey level variance Comb.", featureCombined->ElementAt(i))); + break; + case TextureFilterType::RunLengthVariance : + featureList.push_back(std::make_pair(params.featurePrefix + "Run length variance Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Run length variance Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Run length variance Comb.", featureCombined->ElementAt(i))); + break; + case TextureFilterType::RunEntropy : + featureList.push_back(std::make_pair(params.featurePrefix + "Run length entropy Means", featureMeans->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Run length entropy Std.", featureStd->ElementAt(i))); + featureList.push_back(std::make_pair(params.featurePrefix + "Run length entropy Comb.", featureCombined->ElementAt(i))); + break; + default: + break; + } + } +} + +mitk::GIFGreyLevelRunLength::GIFGreyLevelRunLength() +{ + SetShortName("rl"); + SetLongName("run-length"); + SetFeatureClassName("Run Length"); +} + +mitk::GIFGreyLevelRunLength::FeatureListType mitk::GIFGreyLevelRunLength::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) +{ + InitializeQuantifier(image, mask); + FeatureListType featureList; + + ParameterStruct params; + + params.m_Direction = GetDirection(); + + params.MinimumIntensity = GetQuantifier()->GetMinimum(); + params.MaximumIntensity = GetQuantifier()->GetMaximum(); + params.Bins = GetQuantifier()->GetBins(); + params.featurePrefix = FeatureDescriptionPrefix(); + + MITK_INFO << params.MinimumIntensity; + MITK_INFO << params.MaximumIntensity; + MITK_INFO << params.m_Direction; + MITK_INFO << params.Bins; + + AccessByItk_3(image, CalculateGrayLevelRunLengthFeatures, mask, featureList,params); + + return featureList; +} + +mitk::GIFGreyLevelRunLength::FeatureNameListType mitk::GIFGreyLevelRunLength::GetFeatureNames() +{ + FeatureNameListType featureList; + return featureList; +} + + +void mitk::GIFGreyLevelRunLength::AddArguments(mitkCommandLineParser &parser) +{ + std::string name = GetOptionPrefix(); + + parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Run-Length", "Calculates Run-Length based features", us::Any()); + AddQuantifierArguments(parser); +} + +void +mitk::GIFGreyLevelRunLength::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &, const Image::Pointer &maskNoNAN, FeatureListType &featureList) +{ + auto parsedArgs = GetParameter(); + std::string name = GetOptionPrefix(); + + if (parsedArgs.count(GetLongName())) + { + InitializeQuantifierFromParameters(feature, maskNoNAN); + + MITK_INFO << "Start calculating Run-length"; + auto localResults = this->CalculateFeatures(feature, maskNoNAN); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating Run-length"; + } + +} + +std::string mitk::GIFGreyLevelRunLength::GetCurrentFeatureEncoding() +{ + return QuantifierParameterString(); +} \ No newline at end of file diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFGreyLevelSizeZone.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFGreyLevelSizeZone.cpp new file mode 100644 index 0000000000..2453c82166 --- /dev/null +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFGreyLevelSizeZone.cpp @@ -0,0 +1,371 @@ +#include + +// MITK +#include +#include +#include + +// ITK +#include + +// STL + +static +void MatrixFeaturesTo(mitk::GreyLevelSizeZoneFeatures features, + std::string prefix, + mitk::GIFGreyLevelSizeZone::FeatureListType &featureList); + + + +mitk::GreyLevelSizeZoneMatrixHolder::GreyLevelSizeZoneMatrixHolder(double min, double max, int number, int maxSize) : + m_MinimumRange(min), + m_MaximumRange(max), + m_NumberOfBins(number), + m_MaximumSize(maxSize) +{ + m_Matrix.resize(number, maxSize); + m_Matrix.fill(0); + m_Stepsize = (max - min) / (number); +} + +int mitk::GreyLevelSizeZoneMatrixHolder::IntensityToIndex(double intensity) +{ + return std::floor((intensity - m_MinimumRange) / m_Stepsize); +} + +double mitk::GreyLevelSizeZoneMatrixHolder::IndexToMinIntensity(int index) +{ + return m_MinimumRange + index * m_Stepsize; +} +double mitk::GreyLevelSizeZoneMatrixHolder::IndexToMeanIntensity(int index) +{ + return m_MinimumRange + (index+0.5) * m_Stepsize; +} +double mitk::GreyLevelSizeZoneMatrixHolder::IndexToMaxIntensity(int index) +{ + return m_MinimumRange + (index + 1) * m_Stepsize; +} + +template +static int +CalculateGlSZMatrix(itk::Image* itkImage, + itk::Image* mask, + std::vector > offsets, + bool estimateLargestRegion, + mitk::GreyLevelSizeZoneMatrixHolder &holder) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskImageType; + typedef typename ImageType::IndexType IndexType; + + typedef itk::ImageRegionIteratorWithIndex ConstIterType; + typedef itk::ImageRegionIteratorWithIndex ConstMaskIterType; + + auto region = mask->GetLargestPossibleRegion(); + typename MaskImageType::RegionType newRegion; + newRegion.SetSize(region.GetSize()); + newRegion.SetIndex(region.GetIndex()); + + ConstIterType imageIter(itkImage, itkImage->GetLargestPossibleRegion()); + ConstMaskIterType maskIter(mask, mask->GetLargestPossibleRegion()); + + typename MaskImageType::Pointer visitedImage = MaskImageType::New(); + visitedImage->SetRegions(newRegion); + visitedImage->Allocate(); + visitedImage->FillBuffer(0); + + int largestRegion = 0; + + while (!maskIter.IsAtEnd()) + { + if (maskIter.Value() > 0 ) + { + auto startIntensityIndex = holder.IntensityToIndex(imageIter.Value()); + std::vector indices; + indices.push_back(maskIter.GetIndex()); + unsigned int steps = 0; + + while (indices.size() > 0) + { + auto currentIndex = indices.back(); + indices.pop_back(); + + if (!region.IsInside(currentIndex)) + { + continue; + } + + auto wasVisited = visitedImage->GetPixel(currentIndex); + auto newIntensityIndex = holder.IntensityToIndex(itkImage->GetPixel(currentIndex)); + auto isInMask = mask->GetPixel(currentIndex); + + if ((isInMask > 0) && + (newIntensityIndex == startIntensityIndex) && + (wasVisited < 1)) + { + ++steps; + visitedImage->SetPixel(currentIndex, 1); + for (auto offset : offsets) + { + auto newIndex = currentIndex + offset; + indices.push_back(newIndex); + newIndex = currentIndex - offset; + indices.push_back(newIndex); + } + + } + } + if (steps > 0) + { + largestRegion = std::max(steps, largestRegion); + steps = std::min(steps, holder.m_MaximumSize); + if (!estimateLargestRegion) + { + holder.m_Matrix(startIntensityIndex, steps - 1) += 1; + } + } + } + ++imageIter; + ++maskIter; + } + return largestRegion; +} + +static void CalculateFeatures( + mitk::GreyLevelSizeZoneMatrixHolder &holder, + mitk::GreyLevelSizeZoneFeatures & results + ) +{ + auto SgzMatrix = holder.m_Matrix; + auto pgzMatrix = holder.m_Matrix; + auto pgMatrix = holder.m_Matrix; + auto pzMatrix = holder.m_Matrix; + + double Ns = pgzMatrix.sum(); + pgzMatrix /= Ns; + pgMatrix.rowwise().normalize(); + pzMatrix.colwise().normalize(); + + for (int i = 0; i < holder.m_NumberOfBins; ++i) + for (int j = 0; j < holder.m_NumberOfBins; ++j) + { + if (pgzMatrix(i, j) != pgzMatrix(i, j)) + pgzMatrix(i, j) = 0; + if (pgMatrix(i, j) != pgMatrix(i, j)) + pgMatrix(i, j) = 0; + if (pzMatrix(i, j) != pzMatrix(i, j)) + pzMatrix(i, j) = 0; + } + + Eigen::VectorXd SgVector = SgzMatrix.rowwise().sum(); + Eigen::VectorXd SzVector = SgzMatrix.colwise().sum(); + + for (int j = 0; j < SzVector.size(); ++j) + { + results.SmallZoneEmphasis += SzVector(j) / (j + 1) / (j + 1); + results.LargeZoneEmphasis += SzVector(j) * (j + 1.0) * (j + 1.0); + results.ZoneSizeNonUniformity += SzVector(j) * SzVector(j); + results.ZoneSizeNoneUniformityNormalized += SzVector(j) * SzVector(j); + } + for (int i = 0; i < SgVector.size(); ++i) + { + results.LowGreyLevelEmphasis += SgVector(i) / (i + 1) / (i + 1); + results.HighGreyLevelEmphasis += SgVector(i) * (i + 1) * (i + 1); + results.GreyLevelNonUniformity += SgVector(i)*SgVector(i); + results.GreyLevelNonUniformityNormalized += SgVector(i)*SgVector(i); + } + + for (int i = 0; i < SgzMatrix.rows(); ++i) + { + for (int j = 0; j < SgzMatrix.cols(); ++j) + { + results.SmallZoneLowGreyLevelEmphasis += SgzMatrix(i, j) / (i + 1) / (i + 1) / (j + 1) / (j + 1); + results.SmallZoneHighGreyLevelEmphasis += SgzMatrix(i, j) * (i + 1) * (i + 1) / (j + 1) / (j + 1); + results.LargeZoneLowGreyLevelEmphasis += SgzMatrix(i, j) / (i + 1) / (i + 1) * (j + 1.0) * (j + 1.0); + results.LargeZoneHighGreyLevelEmphasis += SgzMatrix(i, j) * (i + 1) * (i + 1) * (j + 1.0) * (j + 1.0); + results.ZonePercentage += SgzMatrix(i, j)*(j + 1); + + results.GreyLevelMean += (i + 1)*pgzMatrix(i, j); + results.ZoneSizeMean += (j + 1)*pgzMatrix(i, j); + if (pgzMatrix(i, j) > 0) + results.ZoneSizeEntropy -= pgzMatrix(i, j) * std::log(pgzMatrix(i, j)) / std::log(2); + } + } + + for (int i = 0; i < SgzMatrix.rows(); ++i) + { + for (int j = 0; j < SgzMatrix.cols(); ++j) + { + results.GreyLevelVariance += (i + 1 - results.GreyLevelMean)*(i + 1 - results.GreyLevelMean)*pgzMatrix(i, j); + results.ZoneSizeVariance += (j + 1 - results.ZoneSizeMean)*(j + 1 - results.ZoneSizeMean)*pgzMatrix(i, j); + } + } + + results.SmallZoneEmphasis /= Ns; + results.LargeZoneEmphasis /= Ns; + results.LowGreyLevelEmphasis /= Ns; + results.HighGreyLevelEmphasis /= Ns; + + results.SmallZoneLowGreyLevelEmphasis /= Ns; + results.SmallZoneHighGreyLevelEmphasis /= Ns; + results.LargeZoneLowGreyLevelEmphasis /= Ns; + results.LargeZoneHighGreyLevelEmphasis /= Ns; + results.GreyLevelNonUniformity /= Ns; + results.GreyLevelNonUniformityNormalized /= Ns*Ns; + results.ZoneSizeNonUniformity /= Ns; + results.ZoneSizeNoneUniformityNormalized /= Ns*Ns; + + results.ZonePercentage = Ns / results.ZonePercentage; +} + +template +static void +CalculateGreyLevelSizeZoneFeatures(itk::Image* itkImage, mitk::Image::Pointer mask, mitk::GIFGreyLevelSizeZone::FeatureListType & featureList, mitk::GIFGreyLevelSizeZone::GIFGreyLevelSizeZoneConfiguration config) +{ + typedef itk::Image MaskType; + typedef itk::Neighborhood NeighborhoodType; + typedef itk::Offset OffsetType; + + /////////////////////////////////////////////////////////////////////////////////////////////// + double rangeMin = config.MinimumIntensity; + double rangeMax = config.MaximumIntensity; + int numberOfBins = config.Bins; + + typename MaskType::Pointer maskImage = MaskType::New(); + mitk::CastToItkImage(mask, maskImage); + + //Find possible directions + std::vector < itk::Offset > offsetVector; + NeighborhoodType hood; + hood.SetRadius(1); + unsigned int centerIndex = hood.GetCenterNeighborhoodIndex(); + OffsetType offset; + for (unsigned int d = 0; d < centerIndex; d++) + { + offset = hood.GetOffset(d); + bool useOffset = true; + for (unsigned int i = 0; i < VImageDimension; ++i) + { + if ((config.direction == i + 2) && offset[i] != 0) + { + useOffset = false; + } + } + if (useOffset) + { + offsetVector.push_back(offset); + MITK_INFO << offset; + } + } + if (config.direction == 1) + { + offsetVector.clear(); + offset[0] = 0; + offset[1] = 0; + offset[2] = 1; + offsetVector.push_back(offset); + } + + std::vector resultVector; + mitk::GreyLevelSizeZoneMatrixHolder tmpHolder(rangeMin, rangeMax, numberOfBins, 3); + int largestRegion = CalculateGlSZMatrix(itkImage, maskImage, offsetVector, true, tmpHolder); + mitk::GreyLevelSizeZoneMatrixHolder holderOverall(rangeMin, rangeMax, numberOfBins,largestRegion); + mitk::GreyLevelSizeZoneFeatures overallFeature; + CalculateGlSZMatrix(itkImage, maskImage, offsetVector, false, holderOverall); + CalculateFeatures(holderOverall, overallFeature); + + MatrixFeaturesTo(overallFeature, config.prefix, featureList); +} + + +static +void MatrixFeaturesTo(mitk::GreyLevelSizeZoneFeatures features, + std::string prefix, + mitk::GIFGreyLevelSizeZone::FeatureListType &featureList) +{ + featureList.push_back(std::make_pair(prefix + "Small Zone Emphasis", features.SmallZoneEmphasis)); + featureList.push_back(std::make_pair(prefix + "Large Zone Emphasis", features.LargeZoneEmphasis)); + featureList.push_back(std::make_pair(prefix + "Low Grey Level Emphasis", features.LowGreyLevelEmphasis)); + featureList.push_back(std::make_pair(prefix + "High Grey Level Emphasis", features.HighGreyLevelEmphasis)); + featureList.push_back(std::make_pair(prefix + "Small Zone Low Grey Level Emphasis", features.SmallZoneLowGreyLevelEmphasis)); + featureList.push_back(std::make_pair(prefix + "Small Zone High Grey Level Emphasis", features.SmallZoneHighGreyLevelEmphasis)); + featureList.push_back(std::make_pair(prefix + "Large Zone Low Grey Level Emphasis", features.LargeZoneLowGreyLevelEmphasis)); + featureList.push_back(std::make_pair(prefix + "Large Zone High Grey Level Emphasis", features.LargeZoneHighGreyLevelEmphasis)); + featureList.push_back(std::make_pair(prefix + "Grey Level Non-Uniformity", features.GreyLevelNonUniformity)); + featureList.push_back(std::make_pair(prefix + "Grey Level Non-Uniformity Normalized", features.GreyLevelNonUniformityNormalized)); + featureList.push_back(std::make_pair(prefix + "Zone Size Non-Uniformity", features.ZoneSizeNonUniformity)); + featureList.push_back(std::make_pair(prefix + "Zone Size Non-Uniformity Normalized", features.ZoneSizeNoneUniformityNormalized)); + featureList.push_back(std::make_pair(prefix + "Zone Percentage", features.ZonePercentage)); + featureList.push_back(std::make_pair(prefix + "Grey Level Mean", features.GreyLevelMean)); + featureList.push_back(std::make_pair(prefix + "Grey Level Variance", features.GreyLevelVariance)); + featureList.push_back(std::make_pair(prefix + "Zone Size Mean", features.ZoneSizeMean)); + featureList.push_back(std::make_pair(prefix + "Zone Size Variance", features.ZoneSizeVariance)); + featureList.push_back(std::make_pair(prefix + "Zone Size Entropy", features.ZoneSizeEntropy)); +} + + mitk::GIFGreyLevelSizeZone::GIFGreyLevelSizeZone() +{ + SetShortName("glsz"); + SetLongName("grey-level-sizezone"); + SetFeatureClassName("Grey Level Size Zone"); +} + +mitk::GIFGreyLevelSizeZone::FeatureListType mitk::GIFGreyLevelSizeZone::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) +{ + InitializeQuantifier(image, mask); + + FeatureListType featureList; + + GIFGreyLevelSizeZoneConfiguration config; + config.direction = GetDirection(); + + config.MinimumIntensity = GetQuantifier()->GetMinimum(); + config.MaximumIntensity = GetQuantifier()->GetMaximum(); + config.Bins = GetQuantifier()->GetBins(); + config.prefix = FeatureDescriptionPrefix(); + + AccessByItk_3(image, CalculateGreyLevelSizeZoneFeatures, mask, featureList, config); + + return featureList; +} + +mitk::GIFGreyLevelSizeZone::FeatureNameListType mitk::GIFGreyLevelSizeZone::GetFeatureNames() +{ + FeatureNameListType featureList; + return featureList; +} + + + + +void mitk::GIFGreyLevelSizeZone::AddArguments(mitkCommandLineParser &parser) +{ + std::string name = GetOptionPrefix(); + + parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Grey Level Size Zone", "Calculates the size zone based features.", us::Any()); + AddQuantifierArguments(parser); +} + +void +mitk::GIFGreyLevelSizeZone::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &, const Image::Pointer &maskNoNAN, FeatureListType &featureList) +{ + auto parsedArgs = GetParameter(); + std::string name = GetOptionPrefix(); + + if (parsedArgs.count(GetLongName())) + { + InitializeQuantifierFromParameters(feature, maskNoNAN); + + MITK_INFO << "Start calculating Grey leve size zone ..."; + auto localResults = this->CalculateFeatures(feature, maskNoNAN); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating Grey level size zone ..."; + } + +} + +std::string mitk::GIFGreyLevelSizeZone::GetCurrentFeatureEncoding() +{ + return QuantifierParameterString(); +} + diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFImageDescriptionFeatures.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFImageDescriptionFeatures.cpp new file mode 100644 index 0000000000..02d3c28b4b --- /dev/null +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFImageDescriptionFeatures.cpp @@ -0,0 +1,177 @@ +/*=================================================================== + +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 + +// MITK +#include +#include +#include + +// ITK +#include +#include +#include + +// STL +#include + +template +static void +CalculateFirstOrderStatistics(itk::Image* itkImage, mitk::Image::Pointer mask, mitk::GIFImageDescriptionFeatures::FeatureListType & featureList, std::string prefix) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskType; + typedef itk::MinimumMaximumImageCalculator MinMaxComputerType; + + typename MaskType::Pointer maskImage = MaskType::New(); + mitk::CastToItkImage(mask, maskImage); + + unsigned int imageDimensionX = itkImage->GetLargestPossibleRegion().GetSize()[0]; + unsigned int imageDimensionY = itkImage->GetLargestPossibleRegion().GetSize()[1]; + unsigned int imageDimensionZ = itkImage->GetLargestPossibleRegion().GetSize()[2]; + + double imageVoxelSpacingX = itkImage->GetSpacing()[0]; + double imageVoxelSpacingY = itkImage->GetSpacing()[1]; + double imageVoxelSpacingZ = itkImage->GetSpacing()[2]; + + typename MinMaxComputerType::Pointer minMaxComputer = MinMaxComputerType::New(); + minMaxComputer->SetImage(itkImage); + minMaxComputer->Compute(); + + double imageMinimum = minMaxComputer->GetMinimum(); + double imageMaximum = minMaxComputer->GetMaximum(); + + + unsigned int maskDimensionX = maskImage->GetLargestPossibleRegion().GetSize()[0]; + unsigned int maskDimensionY = maskImage->GetLargestPossibleRegion().GetSize()[1]; + unsigned int maskDimensionZ = maskImage->GetLargestPossibleRegion().GetSize()[2]; + + double maskVoxelSpacingX = maskImage->GetSpacing()[0]; + double maskVoxelSpacingY = maskImage->GetSpacing()[1]; + double maskVoxelSpacingZ = maskImage->GetSpacing()[2]; + + + unsigned int voxelCount = 0; + unsigned int maskVoxelCount = 0; + double maskMinimum = imageMaximum; + double maskMaximum = imageMinimum; + double imageMean = 0; + double maskMean = 0; + + unsigned int maskMinimumX = maskDimensionX; + unsigned int maskMaximumX = 0; + unsigned int maskMinimumY = maskDimensionY; + unsigned int maskMaximumY = 0; + unsigned int maskMinimumZ = maskDimensionZ; + unsigned int maskMaximumZ = 0; + + + itk::ImageRegionConstIteratorWithIndex imIter(itkImage, itkImage->GetLargestPossibleRegion()); + itk::ImageRegionConstIteratorWithIndex maIter(maskImage, maskImage->GetLargestPossibleRegion()); + + while (!imIter.IsAtEnd()) + { + auto pixelValue = imIter.Get(); + if (maIter.Get() > 0) + { + ++maskVoxelCount; + maskMean += pixelValue; + maskMinimum = (maskMinimum > pixelValue) ? pixelValue : maskMinimum; + maskMaximum = (maskMaximum < pixelValue) ? pixelValue : maskMaximum; + maskMinimumX = (maskMinimumX > imIter.GetIndex()[0]) ? imIter.GetIndex()[0] : maskMinimumX; + maskMaximumX = (maskMaximumX < imIter.GetIndex()[0]) ? imIter.GetIndex()[0] : maskMaximumX; + maskMinimumY = (maskMinimumY > imIter.GetIndex()[1]) ? imIter.GetIndex()[1] : maskMinimumY; + maskMaximumY = (maskMaximumY < imIter.GetIndex()[1]) ? imIter.GetIndex()[1] : maskMaximumY; + maskMinimumZ = (maskMinimumZ > imIter.GetIndex()[2]) ? imIter.GetIndex()[2] : maskMinimumZ; + maskMaximumZ = (maskMaximumZ < imIter.GetIndex()[2]) ? imIter.GetIndex()[2] : maskMaximumZ; + } + ++voxelCount; + imageMean += pixelValue; + ++imIter; + ++maIter; + } + imageMean /= voxelCount; + maskMean /= maskVoxelCount; + + featureList.push_back(std::make_pair(prefix + "Image Dimension X", imageDimensionX)); + featureList.push_back(std::make_pair(prefix + "Image Dimension Y", imageDimensionY)); + featureList.push_back(std::make_pair(prefix + "Image Dimension Z", imageDimensionZ)); + featureList.push_back(std::make_pair(prefix + "Image Spacing X", imageVoxelSpacingX)); + featureList.push_back(std::make_pair(prefix + "Image Spacing Y", imageVoxelSpacingY)); + featureList.push_back(std::make_pair(prefix + "Image Spacing Z", imageVoxelSpacingZ)); + featureList.push_back(std::make_pair(prefix + "Image Mean intensity", imageMean)); + featureList.push_back(std::make_pair(prefix + "Image Minimum intensity", imageMinimum)); + featureList.push_back(std::make_pair(prefix + "Image Maximum intensity", imageMaximum)); + + featureList.push_back(std::make_pair(prefix + "Mask Dimension X", maskDimensionX)); + featureList.push_back(std::make_pair(prefix + "Mask Dimension Y", maskDimensionY)); + featureList.push_back(std::make_pair(prefix + "Mask Dimension Z", maskDimensionZ)); + featureList.push_back(std::make_pair(prefix + "Mask bounding box X", maskMaximumX - maskMinimumX + 1)); + featureList.push_back(std::make_pair(prefix + "Mask bounding box Y", maskMaximumY - maskMinimumY + 1)); + featureList.push_back(std::make_pair(prefix + "Mask bounding box Z", maskMaximumZ - maskMinimumZ + 1)); + featureList.push_back(std::make_pair(prefix + "Mask Spacing X", maskVoxelSpacingX)); + featureList.push_back(std::make_pair(prefix + "Mask Spacing Y", maskVoxelSpacingY)); + featureList.push_back(std::make_pair(prefix + "Mask Spacing Z", maskVoxelSpacingZ)); + featureList.push_back(std::make_pair(prefix + "Mask Voxel Count ", maskVoxelCount)); + featureList.push_back(std::make_pair(prefix + "Mask Mean intensity", maskMean)); + featureList.push_back(std::make_pair(prefix + "Mask Minimum intensity", maskMinimum)); + featureList.push_back(std::make_pair(prefix + "Mask Maximum intensity", maskMaximum)); + +} + +mitk::GIFImageDescriptionFeatures::GIFImageDescriptionFeatures() +{ + SetShortName("id"); + SetLongName("image-diagnostic"); + + SetFeatureClassName("Diagnostic"); +} + +mitk::GIFImageDescriptionFeatures::FeatureListType mitk::GIFImageDescriptionFeatures::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) +{ + FeatureListType featureList; + AccessByItk_3(image, CalculateFirstOrderStatistics, mask, featureList, FeatureDescriptionPrefix()); + + return featureList; +} + +mitk::GIFImageDescriptionFeatures::FeatureNameListType mitk::GIFImageDescriptionFeatures::GetFeatureNames() +{ + FeatureNameListType featureList; + return featureList; +} + + +void mitk::GIFImageDescriptionFeatures::AddArguments(mitkCommandLineParser &parser) +{ + std::string name = GetOptionPrefix(); + parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Image Description", "calculates image description features", us::Any()); +} + +void +mitk::GIFImageDescriptionFeatures::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &, const Image::Pointer &maskNoNAN, FeatureListType &featureList) +{ + auto parsedArgs = GetParameter(); + if (parsedArgs.count(GetLongName())) + { + MITK_INFO << "Start calculating image description features...."; + auto localResults = this->CalculateFeatures(feature, maskNoNAN); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating image description features...."; + } +} + diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFIntensityVolumeHistogramFeatures.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFIntensityVolumeHistogramFeatures.cpp new file mode 100644 index 0000000000..d29b4a9e0e --- /dev/null +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFIntensityVolumeHistogramFeatures.cpp @@ -0,0 +1,173 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include + +// MITK +#include +#include +#include +#include + +// ITK +#include +#include +// STL +#include + +struct GIFIntensityVolumeHistogramFeaturesParameters +{ + mitk::IntensityQuantifier::Pointer quantifier; + std::string prefix; +}; + + +template +static void +CalculateIntensityPeak(itk::Image* itkImage, mitk::Image::Pointer mask, GIFIntensityVolumeHistogramFeaturesParameters params, mitk::GIFIntensityVolumeHistogramFeatures::FeatureListType & featureList) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskType; + + typename MaskType::Pointer itkMask = MaskType::New(); + mitk::CastToItkImage(mask, itkMask); + + mitk::IntensityQuantifier::Pointer quantifier = params.quantifier; + std::string prefix = params.prefix; + + itk::ImageRegionConstIterator iter(itkImage, itkImage->GetLargestPossibleRegion()); + itk::ImageRegionConstIterator iterMask(itkMask, itkMask->GetLargestPossibleRegion()); + + MITK_INFO << "Quantification: " << quantifier->GetMinimum() << " to " << quantifier->GetMaximum() << " with " << quantifier->GetBins()<< " bins"; + + iter.GoToBegin(); + iterMask.GoToBegin(); + std::vector hist; + hist.resize(quantifier->GetBins() , 0); + + int count = 0; + while (!iter.IsAtEnd()) + { + if (iterMask.Get() > 0) + { + double value = iter.Get(); + //std::size_t index = std::floor((value - minimum) / (maximum - minimum) * (bins-1)); + std::size_t index = quantifier->IntensityToIndex(value); + ++count; + hist[index] += 1.0;// / count; + } + ++iterMask; + ++iter; + } + + bool notFoundIntenstiy010 = true; + bool notFoundIntenstiy090 = true; + + double intensity010 = -1; + double intensity090 = -1; + double fraction = 0; + double auc = 0; + bool firstRound = true; + for (int i = quantifier->GetBins()-1; i >= 0; --i) + { + hist[i] /= count; + hist[i] += fraction; + fraction = hist[i]; + if (!firstRound) + { + auc += 0.5 * (hist[i] + hist[i+1]) / (quantifier->GetBins()-1); + } + firstRound = false; + + if (notFoundIntenstiy010 && fraction > 0.1) + { + intensity010 = quantifier->IndexToMeanIntensity(i + 1); + notFoundIntenstiy010 = false; + } + if (notFoundIntenstiy090 && fraction > 0.9) + { + intensity090 = quantifier->IndexToMeanIntensity(i + 1); + notFoundIntenstiy090 = false; + } + } + + unsigned int index010 = std::ceil(quantifier->GetBins() * 0.1); + unsigned int index090 = std::floor(quantifier->GetBins() * 0.9); + + featureList.push_back(std::make_pair(prefix + "Volume fration at 0.10 intensity", hist[index010])); + featureList.push_back(std::make_pair(prefix + "Volume fration at 0.90 intensity", hist[index090])); + featureList.push_back(std::make_pair(prefix + "Intensity at 0.10 volume", intensity010)); + featureList.push_back(std::make_pair(prefix + "Intensity at 0.90 volume", intensity090)); + featureList.push_back(std::make_pair(prefix + "Difference volume fration at 0.10 and 0.90 intensity", std::abs(hist[index010] - hist[index090]))); + featureList.push_back(std::make_pair(prefix + "Difference intensity at 0.10 and 0.90 volume", std::abs(intensity090 - intensity010))); + featureList.push_back(std::make_pair(prefix + "Area under IVH curve", auc)); + //featureList.push_back(std::make_pair("Local Intensity Global Intensity Peak", globalPeakValue)); +} + + +mitk::GIFIntensityVolumeHistogramFeatures::GIFIntensityVolumeHistogramFeatures() +{ + SetLongName("intensity-volume-histogram"); + SetShortName("ivoh"); + SetFeatureClassName("Intensity Volume Histogram"); +} + +mitk::GIFIntensityVolumeHistogramFeatures::FeatureListType mitk::GIFIntensityVolumeHistogramFeatures::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) +{ + InitializeQuantifier(image, mask, 1000); + FeatureListType featureList; + GIFIntensityVolumeHistogramFeaturesParameters params; + params.quantifier = GetQuantifier(); + params.prefix = FeatureDescriptionPrefix(); + AccessByItk_3(image, CalculateIntensityPeak, mask, params, featureList); + return featureList; +} + +mitk::GIFIntensityVolumeHistogramFeatures::FeatureNameListType mitk::GIFIntensityVolumeHistogramFeatures::GetFeatureNames() +{ + FeatureNameListType featureList; + return featureList; +} + + +void mitk::GIFIntensityVolumeHistogramFeatures::AddArguments(mitkCommandLineParser &parser) +{ + std::string name = GetOptionPrefix(); + + parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Local Intensity", "calculates local intensity based features", us::Any()); + AddQuantifierArguments(parser); +} + +void +mitk::GIFIntensityVolumeHistogramFeatures::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &, FeatureListType &featureList) +{ + InitializeQuantifierFromParameters(feature, mask, 1000); + + auto parsedArgs = GetParameter(); + if (parsedArgs.count(GetLongName())) + { + MITK_INFO << "Start calculating local intensity features ...."; + auto localResults = this->CalculateFeatures(feature, mask); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating local intensity features...."; + } +} + +std::string mitk::GIFIntensityVolumeHistogramFeatures::GetCurrentFeatureEncoding() +{ + return QuantifierParameterString(); +} + diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFLocalIntensity.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFLocalIntensity.cpp new file mode 100644 index 0000000000..d364e3df15 --- /dev/null +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFLocalIntensity.cpp @@ -0,0 +1,179 @@ +/*=================================================================== + +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 + +// MITK +#include +#include +#include +#include +#include + +// ITK +#include +#include +// STL +#include + +struct GIFLocalIntensityParameter +{ + double range; + std::string prefix; +}; + + +template +static void +CalculateIntensityPeak(itk::Image* itkImage, mitk::Image::Pointer mask, mitk::GIFLocalIntensity::FeatureListType & featureList, GIFLocalIntensityParameter params) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskType; + + typename MaskType::Pointer itkMask = MaskType::New(); + mitk::CastToItkImage(mask, itkMask); + + double range = params.range; + double minimumSpacing = std::numeric_limits::max(); + itkImage->GetSpacing(); + for (unsigned int i = 0; i < VImageDimension; ++i) + { + minimumSpacing = (minimumSpacing < itkImage->GetSpacing()[i]) ? minimumSpacing : itkImage->GetSpacing()[i]; + } + typename ImageType::SizeType regionSize; + int offset = std::ceil(range / minimumSpacing); + regionSize.Fill(offset); + + itk::NeighborhoodIterator iter(regionSize, itkImage, itkImage->GetLargestPossibleRegion()); + itk::NeighborhoodIterator iterMask(regionSize, itkMask, itkMask->GetLargestPossibleRegion()); + + typename ImageType::PointType origin; + typename ImageType::PointType localPoint; + itk::Index index; + + double tmpPeakValue; + double globalPeakValue = 0; + double localPeakValue = 0; + TPixel localMaximum = 0; + + int count = 0; + while (!iter.IsAtEnd()) + { + if (iterMask.GetCenterPixel() > 0) + { + tmpPeakValue = 0; + count = 0; + index = iter.GetIndex(); + itkImage->TransformIndexToPhysicalPoint(index, origin); + for (itk::SizeValueType i = 0; i < iter.Size(); ++i) + { + itkImage->TransformIndexToPhysicalPoint(iter.GetIndex(i), localPoint); + double dist = origin.EuclideanDistanceTo(localPoint); + if (dist < 6.2) + { + if (iter.IndexInBounds(i)) + { + tmpPeakValue += iter.GetPixel(i); + ++count; + } + } + } + tmpPeakValue /= count; + globalPeakValue = std::max(tmpPeakValue, globalPeakValue); + if (localMaximum == iter.GetCenterPixel()) + { + localPeakValue = std::max(tmpPeakValue,localPeakValue); + } + else if (localMaximum < iter.GetCenterPixel()) + { + localMaximum = iter.GetCenterPixel(); + localPeakValue = tmpPeakValue; + } + } + ++iterMask; + ++iter; + } + featureList.push_back(std::make_pair(params.prefix + "Local Intensity Peak", localPeakValue)); + featureList.push_back(std::make_pair(params.prefix + "Global Intensity Peak", globalPeakValue)); +} + + +mitk::GIFLocalIntensity::GIFLocalIntensity() : +m_Range(6.2) +{ + SetLongName("local-intensity"); + SetShortName("loci"); + SetFeatureClassName("Local Intensity"); +} + +mitk::GIFLocalIntensity::FeatureListType mitk::GIFLocalIntensity::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) +{ + FeatureListType featureList; + if (image->GetDimension() < 3) + { + return featureList; + } + GIFLocalIntensityParameter params; + params.range = GetRange(); + params.prefix = FeatureDescriptionPrefix(); + AccessByItk_3(image, CalculateIntensityPeak, mask, featureList, params); + return featureList; +} + +mitk::GIFLocalIntensity::FeatureNameListType mitk::GIFLocalIntensity::GetFeatureNames() +{ + FeatureNameListType featureList; + return featureList; +} + + +void mitk::GIFLocalIntensity::AddArguments(mitkCommandLineParser &parser) +{ + std::string name = GetOptionPrefix(); + + parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Local Intensity", "calculates local intensity based features", us::Any()); + parser.addArgument(name + "::range", name+"::range", mitkCommandLineParser::Float, "Range for the local intensity", "Give the range that should be used for the local intensity in mm", us::Any()); +} + +void +mitk::GIFLocalIntensity::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &, FeatureListType &featureList) +{ + std::string name = GetOptionPrefix(); + auto parsedArgs = GetParameter(); + if (parsedArgs.count(GetLongName())) + { + if (parsedArgs.count(name + "::range")) + { + double range = us::any_cast(parsedArgs[name + "::range"]); + this->SetRange(range); + } + MITK_INFO << "Start calculating local intensity features ...."; + auto localResults = this->CalculateFeatures(feature, mask); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating local intensity features...."; + } +} + +std::string mitk::GIFLocalIntensity::GetCurrentFeatureEncoding() +{ + std::ostringstream ss; + ss << m_Range; + std::string strRange = ss.str(); + return "Range-" + ss.str(); +} + + + diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbourhoodGreyLevelDifference.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbourhoodGreyLevelDifference.cpp new file mode 100644 index 0000000000..47fe2d6e2a --- /dev/null +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbourhoodGreyLevelDifference.cpp @@ -0,0 +1,224 @@ +/*=================================================================== + +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 + +// MITK +#include +#include +#include + +// ITK +#include +#include + +// STL +#include + +template +void + CalculateGrayLevelNeighbourhoodGreyLevelDifferenceFeatures(itk::Image* itkImage, mitk::Image::Pointer mask, mitk::GIFNeighbourhoodGreyLevelDifference::FeatureListType & featureList, mitk::GIFNeighbourhoodGreyLevelDifference::ParameterStruct params) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskType; + typedef itk::Statistics::EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter FilterType; + typedef itk::MinimumMaximumImageCalculator MinMaxComputerType; + typedef typename FilterType::NeighbourhoodGreyLevelDifferenceFeaturesFilterType TextureFilterType; + + typename MaskType::Pointer maskImage = MaskType::New(); + mitk::CastToItkImage(mask, maskImage); + + typename FilterType::Pointer filter = FilterType::New(); + + typename FilterType::OffsetVector::Pointer newOffset = FilterType::OffsetVector::New(); + auto oldOffsets = filter->GetOffsets(); + auto oldOffsetsIterator = oldOffsets->Begin(); + while (oldOffsetsIterator != oldOffsets->End()) + { + bool continueOuterLoop = false; + typename FilterType::OffsetType offset = oldOffsetsIterator->Value(); + for (unsigned int i = 0; i < VImageDimension; ++i) + { + if (params.m_Direction == i + 2 && offset[i] != 0) + { + continueOuterLoop = true; + } + } + if (params.m_Direction == 1) + { + offset[0] = 0; + offset[1] = 0; + offset[2] = 1; + newOffset->push_back(offset); + break; + } + + oldOffsetsIterator++; + if (continueOuterLoop) + continue; + newOffset->push_back(offset); + } + filter->SetOffsets(newOffset); + + + // All features are required + typename FilterType::FeatureNameVectorPointer requestedFeatures = FilterType::FeatureNameVector::New(); + requestedFeatures->push_back(TextureFilterType::Coarseness); + requestedFeatures->push_back(TextureFilterType::Contrast); + requestedFeatures->push_back(TextureFilterType::Busyness); + requestedFeatures->push_back(TextureFilterType::Complexity); + requestedFeatures->push_back(TextureFilterType::Strength); + + typename MinMaxComputerType::Pointer minMaxComputer = MinMaxComputerType::New(); + minMaxComputer->SetImage(itkImage); + minMaxComputer->Compute(); + + filter->SetInput(itkImage); + filter->SetMaskImage(maskImage); + filter->SetRequestedFeatures(requestedFeatures); + int rangeOfPixels = params.m_Range; + if (rangeOfPixels < 2) + rangeOfPixels = 256; + + if (params.m_UseCtRange) + { + filter->SetPixelValueMinMax((TPixel)(-1024.5),(TPixel)(3096.5)); + filter->SetNumberOfBinsPerAxis(3096.5+1024.5); + } else + { + filter->SetPixelValueMinMax(minMaxComputer->GetMinimum(),minMaxComputer->GetMaximum()); + filter->SetNumberOfBinsPerAxis(rangeOfPixels); + } + + filter->SetDistanceValueMinMax(0,rangeOfPixels); + + filter->Update(); + + auto featureMeans = filter->GetFeatureMeans (); + auto featureStd = filter->GetFeatureStandardDeviations(); + + std::ostringstream ss; + ss << rangeOfPixels; + std::string strRange = ss.str(); + for (std::size_t i = 0; i < featureMeans->size(); ++i) + { + switch (i) + { + case TextureFilterType::Coarseness : + featureList.push_back(std::make_pair("NeighbourhoodGreyLevelDifference ("+ strRange+") Coarseness Means",featureMeans->ElementAt(i))); + break; + case TextureFilterType::Contrast : + featureList.push_back(std::make_pair("NeighbourhoodGreyLevelDifference ("+ strRange+") Contrast Means",featureMeans->ElementAt(i))); + break; + case TextureFilterType::Busyness : + featureList.push_back(std::make_pair("NeighbourhoodGreyLevelDifference ("+ strRange+") Busyness Means",featureMeans->ElementAt(i))); + break; + case TextureFilterType::Complexity : + featureList.push_back(std::make_pair("NeighbourhoodGreyLevelDifference ("+ strRange+") Complexity Means",featureMeans->ElementAt(i))); + break; + case TextureFilterType::Strength : + featureList.push_back(std::make_pair("NeighbourhoodGreyLevelDifference ("+ strRange+") Strength Means",featureMeans->ElementAt(i))); + break; + default: + break; + } + } +} + +mitk::GIFNeighbourhoodGreyLevelDifference::GIFNeighbourhoodGreyLevelDifference(): +m_Range(1.0), m_UseCtRange(false) +{ + SetShortName("ngld"); + SetLongName("NeighbourhoodGreyLevelDifference"); +} + +mitk::GIFNeighbourhoodGreyLevelDifference::FeatureListType mitk::GIFNeighbourhoodGreyLevelDifference::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) +{ + FeatureListType featureList; + + ParameterStruct params; + params.m_UseCtRange=m_UseCtRange; + params.m_Range = m_Range; + params.m_Direction = GetDirection(); + + AccessByItk_3(image, CalculateGrayLevelNeighbourhoodGreyLevelDifferenceFeatures, mask, featureList,params); + + return featureList; +} + +mitk::GIFNeighbourhoodGreyLevelDifference::FeatureNameListType mitk::GIFNeighbourhoodGreyLevelDifference::GetFeatureNames() +{ + FeatureNameListType featureList; + featureList.push_back("NeighbourhoodGreyLevelDifference. Coarseness Means"); + featureList.push_back("NeighbourhoodGreyLevelDifference. Coarseness Std."); + featureList.push_back("NeighbourhoodGreyLevelDifference. Contrast Means"); + featureList.push_back("NeighbourhoodGreyLevelDifference. Contrast Std."); + featureList.push_back("NeighbourhoodGreyLevelDifference. Busyness Means"); + featureList.push_back("NeighbourhoodGreyLevelDifference. Busyness Std."); + featureList.push_back("NeighbourhoodGreyLevelDifference. Complexity Means"); + featureList.push_back("NeighbourhoodGreyLevelDifference. Complexity Std."); + featureList.push_back("NeighbourhoodGreyLevelDifference. Strength Means"); + featureList.push_back("NeighbourhoodGreyLevelDifference. Strength Std."); + return featureList; +} + + + +void mitk::GIFNeighbourhoodGreyLevelDifference::AddArguments(mitkCommandLineParser &parser) +{ + std::string name = GetOptionPrefix(); + + parser.addArgument(GetLongName(), name, mitkCommandLineParser::String, "Use Co-occurence matrix", "calculates Co-occurence based features (new implementation)", us::Any()); + parser.addArgument(name + "::range", name + "::range", mitkCommandLineParser::String, "Cooc 2 Range", "Define the range that is used (Semicolon-separated)", us::Any()); + parser.addArgument(name + "::direction", name + "::dir", mitkCommandLineParser::String, "Int", "Allows to specify the direction for Cooc and RL. 0: All directions, 1: Only single direction (Test purpose), 2,3,4... without dimension 0,1,2... ", us::Any()); +} + +void +mitk::GIFNeighbourhoodGreyLevelDifference::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &, const Image::Pointer &maskNoNAN, FeatureListType &featureList) +{ + auto parsedArgs = GetParameter(); + std::string name = GetOptionPrefix(); + + if (parsedArgs.count(GetLongName())) + { + int direction = 0; + if (parsedArgs.count(name + "::direction")) + { + direction = SplitDouble(parsedArgs[name + "::direction"].ToString(), ';')[0]; + } + std::vector ranges; + if (parsedArgs.count(name + "::range")) + { + ranges = SplitDouble(parsedArgs[name + "::range"].ToString(), ';'); + } + else + { + ranges.push_back(1); + } + + for (std::size_t i = 0; i < ranges.size(); ++i) + { + MITK_INFO << "Start calculating Neighbourhood Grey Level Difference with range " << ranges[i] << "...."; + this->SetRange(ranges[i]); + this->SetDirection(direction); + auto localResults = this->CalculateFeatures(feature, maskNoNAN); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating coocurence with range " << ranges[i] << "...."; + } + } + +} + diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.cpp new file mode 100644 index 0000000000..70a6837f27 --- /dev/null +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.cpp @@ -0,0 +1,221 @@ +/*=================================================================== + +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 + +// MITK +#include +#include +#include +#include +#include + +// ITK +#include +#include +// STL +#include + +struct GIFNeighbourhoodGreyToneDifferenceParameter +{ + int Range = 1; + mitk::IntensityQuantifier::Pointer quantifier; + std::string prefix; +}; + +template +static void +CalculateIntensityPeak(itk::Image* itkImage, mitk::Image::Pointer mask, GIFNeighbourhoodGreyToneDifferenceParameter params, mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::FeatureListType & featureList) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskType; + + typename MaskType::Pointer itkMask = MaskType::New(); + mitk::CastToItkImage(mask, itkMask); + + typename ImageType::SizeType regionSize; + regionSize.Fill(params.Range); + + itk::NeighborhoodIterator iter(regionSize, itkImage, itkImage->GetLargestPossibleRegion()); + itk::NeighborhoodIterator iterMask(regionSize, itkMask, itkMask->GetLargestPossibleRegion()); + + std::vector pVector; + std::vector sVector; + pVector.resize(params.quantifier->GetBins(), 0); + sVector.resize(params.quantifier->GetBins(), 0); + + int count = 0; + while (!iter.IsAtEnd()) + { + if (iterMask.GetCenterPixel() > 0) + { + int localCount = 0; + double localMean = 0; + unsigned int localIndex = params.quantifier->IntensityToIndex(iter.GetCenterPixel()); + for (itk::SizeValueType i = 0; i < iter.Size(); ++i) + { + if (i == (iter.Size() / 2)) + continue; + if (iterMask.GetPixel(i) > 0) + { + ++localCount; + localMean += params.quantifier->IntensityToIndex(iter.GetPixel(i)) + 1; + } + } + if (localCount > 0) + { + localMean /= localCount; + } + localMean = std::abs(localIndex + 1 - localMean); + + pVector[localIndex] += 1; + sVector[localIndex] += localMean; + ++count; + + + } + ++iterMask; + ++iter; + } + + unsigned int Ngp = 0; + for (unsigned int i = 0; i < params.quantifier->GetBins(); ++i) + { + if (pVector[i] > 0.1) + { + ++Ngp; + } + pVector[i] /= count; + } + + double sumS = 0; + double sumStimesP = 0; + + double contrastA = 0; + double busynessA = 0; + double complexity = 0; + double strengthA = 0; + for (unsigned int i = 0; i < params.quantifier->GetBins(); ++i) + { + sumS += sVector[i]; + sumStimesP += pVector[i] * sVector[i]; + for (unsigned int j = 0; j < params.quantifier->GetBins(); ++j) + { + double iMinusj = 1.0*i - 1.0*j; + contrastA += pVector[i] * pVector[j] * iMinusj*iMinusj; + if ((pVector[i] > 0) && (pVector[j] > 0)) + { + busynessA += std::abs((i + 1.0)*pVector[i] - (j + 1.0)*pVector[j]); + complexity += std::abs(iMinusj)*(pVector[i] * sVector[i] + pVector[j] * sVector[j]) / (pVector[i] + pVector[j]); + strengthA += (pVector[i] + pVector[j])*iMinusj*iMinusj; + } + } + } + double coarsness = 1.0 / std::min(sumStimesP, 1000000); + double contrast = 0; + double busyness = 0; + if (Ngp > 1) + { + contrast = contrastA / Ngp / (Ngp - 1) / count * sumS; + busyness = sumStimesP / busynessA; + } + complexity /= count; + double strength = 0; + if (sumS > 0) + { + strength = strengthA / sumS; + } + + std::string prefix = params.prefix; + featureList.push_back(std::make_pair(prefix + "Coarsness", coarsness)); + featureList.push_back(std::make_pair(prefix + "Contrast", contrast)); + featureList.push_back(std::make_pair(prefix + "Busyness", busyness)); + featureList.push_back(std::make_pair(prefix + "Complexity", complexity)); + featureList.push_back(std::make_pair(prefix + "Strength", strength)); +} + + +mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::GIFNeighbourhoodGreyToneDifferenceFeatures() : +m_Range(1) +{ + SetLongName("neighbourhood-grey-tone-difference"); + SetShortName("ngtd"); + SetFeatureClassName("Neighbourhood Grey Tone Difference"); +} + +mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::FeatureListType mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) +{ + FeatureListType featureList; + + InitializeQuantifierFromParameters(image, mask); + + GIFNeighbourhoodGreyToneDifferenceParameter params; + params.Range = GetRange(); + params.quantifier = GetQuantifier(); + params.prefix = FeatureDescriptionPrefix(); + + AccessByItk_3(image, CalculateIntensityPeak, mask, params, featureList); + return featureList; +} + +mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::FeatureNameListType mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::GetFeatureNames() +{ + FeatureNameListType featureList; + return featureList; +} + + +void mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::AddArguments(mitkCommandLineParser &parser) +{ + AddQuantifierArguments(parser); + std::string name = GetOptionPrefix(); + + parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Neighbourhood Grey Tone Difference", "calculates Neighborhood Grey Tone based features", us::Any()); + parser.addArgument(name + "::range", name + "::range", mitkCommandLineParser::Int, "Range for the local intensity", "Give the range that should be used for the local intensity in mm", us::Any()); + +} + +void +mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &, FeatureListType &featureList) +{ + InitializeQuantifierFromParameters(feature, mask); + std::string name = GetOptionPrefix(); + + auto parsedArgs = GetParameter(); + if (parsedArgs.count(GetLongName())) + { + MITK_INFO << "Start calculating Neighbourhood Grey Tone Difference features ...."; + if (parsedArgs.count(name + "::range")) + { + int range = us::any_cast(parsedArgs[name + "::range"]); + this->SetRange(range); + } + auto localResults = this->CalculateFeatures(feature, mask); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating Neighbourhood Grey Tone Difference features...."; + } +} + +std::string mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::GetCurrentFeatureEncoding() +{ + std::ostringstream ss; + ss << m_Range; + std::string strRange = ss.str(); + return QuantifierParameterString() + "_Range-" + ss.str(); +} + + + diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbouringGreyLevelDependenceFeatures.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbouringGreyLevelDependenceFeatures.cpp new file mode 100644 index 0000000000..7ac3709f6f --- /dev/null +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbouringGreyLevelDependenceFeatures.cpp @@ -0,0 +1,398 @@ +#include + +// MITK +#include +#include +#include + +// ITK +#include +#include +#include +#include + +// STL +#include + +static +void MatrixFeaturesTo(mitk::NGLDMMatrixFeatures features, + std::string prefix, + mitk::GIFNeighbouringGreyLevelDependenceFeature::FeatureListType &featureList); + + +mitk::NGLDMMatrixHolder::NGLDMMatrixHolder(double min, double max, int number, int depenence) : + m_MinimumRange(min), + m_MaximumRange(max), + m_Stepsize(0), + m_NumberOfDependences(depenence), + m_NumberOfBins(number), + m_NeighbourhoodSize(1), + m_NumberOfNeighbourVoxels(0), + m_NumberOfDependenceNeighbourVoxels(0), + m_NumberOfNeighbourhoods(0), + m_NumberOfCompleteNeighbourhoods(0) +{ + m_Matrix.resize(number, depenence); + m_Matrix.fill(0); + m_Stepsize = (max - min) / (number); +} + +int mitk::NGLDMMatrixHolder::IntensityToIndex(double intensity) +{ + return std::floor((intensity - m_MinimumRange) / m_Stepsize); +} + +double mitk::NGLDMMatrixHolder::IndexToMinIntensity(int index) +{ + return m_MinimumRange + index * m_Stepsize; +} +double mitk::NGLDMMatrixHolder::IndexToMeanIntensity(int index) +{ + return m_MinimumRange + (index+0.5) * m_Stepsize; +} +double mitk::NGLDMMatrixHolder::IndexToMaxIntensity(int index) +{ + return m_MinimumRange + (index + 1) * m_Stepsize; +} + +template +void +CalculateNGLDMMatrix(itk::Image* itkImage, + itk::Image* mask, + int alpha, + int range, + unsigned int direction, + mitk::NGLDMMatrixHolder &holder) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskImageType; + typedef itk::NeighborhoodIterator ShapeIterType; + typedef itk::NeighborhoodIterator ShapeMaskIterType; + + holder.m_NumberOfCompleteNeighbourhoods = 0; + holder.m_NumberOfNeighbourhoods = 0; + holder.m_NumberOfNeighbourVoxels = 0; + holder.m_NumberOfDependenceNeighbourVoxels = 0; + + itk::Size radius; + radius.Fill(range); + + if ((direction > 1) && (direction - 2 GetLargestPossibleRegion()); + ShapeMaskIterType maskIter(radius, mask, mask->GetLargestPossibleRegion()); + + auto region = mask->GetLargestPossibleRegion(); + + auto center = imageIter.Size() / 2; + auto iterSize = imageIter.Size(); + holder.m_NeighbourhoodSize = iterSize-1; + while (!maskIter.IsAtEnd()) + { + int sameValues = 0; + bool completeNeighbourhood = true; + + int i = holder.IntensityToIndex(imageIter.GetCenterPixel()); + + if ((imageIter.GetCenterPixel() != imageIter.GetCenterPixel()) || + (maskIter.GetCenterPixel() < 1)) + { + ++imageIter; + ++maskIter; + continue; + } + + for (unsigned int position = 0; position < iterSize; ++position) + { + if (position == center) + { + continue; + } + if ( ! region.IsInside(maskIter.GetIndex(position))) + { + completeNeighbourhood = false; + continue; + } + bool isInBounds; + auto jIntensity = imageIter.GetPixel(position, isInBounds); + auto jMask = maskIter.GetPixel(position, isInBounds); + if (jMask < 1 || (jIntensity != jIntensity) || ( ! isInBounds)) + { + completeNeighbourhood = false; + continue; + } + + int j = holder.IntensityToIndex(jIntensity); + holder.m_NumberOfNeighbourVoxels += 1; + if (std::abs(i - j) <= alpha) + { + holder.m_NumberOfDependenceNeighbourVoxels += 1; + ++sameValues; + } + } + holder.m_Matrix(i, sameValues) += 1; + holder.m_NumberOfNeighbourhoods += 1; + if (completeNeighbourhood) + { + holder.m_NumberOfCompleteNeighbourhoods += 1; + } + + ++imageIter; + ++maskIter; + } + +} + +void LocalCalculateFeatures( + mitk::NGLDMMatrixHolder &holder, + mitk::NGLDMMatrixFeatures & results + ) +{ + auto sijMatrix = holder.m_Matrix; + auto piMatrix = holder.m_Matrix; + auto pjMatrix = holder.m_Matrix; + // double Ng = holder.m_NumberOfBins; + // int NgSize = holder.m_NumberOfBins; + double Ns = sijMatrix.sum(); + piMatrix.rowwise().normalize(); + pjMatrix.colwise().normalize(); + + for (int i = 0; i < holder.m_NumberOfBins; ++i) + { + double sj = 0; + for (int j = 0; j < holder.m_NumberOfDependences; ++j) + { + double iInt = i+1 ;// holder.IndexToMeanIntensity(i); + double sij = sijMatrix(i, j); + double k = j + 1; + double pij = sij / Ns; + + results.LowDependenceEmphasis += sij / k / k; + results.HighDependenceEmphasis += sij * k*k; + if (iInt != 0) + { + results.LowGreyLevelCountEmphasis += sij / iInt / iInt; + } + results.HighGreyLevelCountEmphasis += sij * iInt*iInt; + if (iInt != 0) + { + results.LowDependenceLowGreyLevelEmphasis += sij / k / k / iInt / iInt; + } + results.LowDependenceHighGreyLevelEmphasis += sij * iInt*iInt / k / k; + if (iInt != 0) + { + results.HighDependenceLowGreyLevelEmphasis += sij *k * k / iInt / iInt; + } + results.HighDependenceHighGreyLevelEmphasis += sij * k*k*iInt*iInt; + + + results.MeanGreyLevelCount += iInt * pij; + results.MeanDependenceCount += k * pij; + if (pij > 0) + { + results.DependenceCountEntropy -= pij * std::log(pij) / std::log(2); + } + results.DependenceCountEnergy += pij*pij; + sj += sij; + } + results.GreyLevelNonUniformity += sj*sj; + results.GreyLevelNonUniformityNormalised += sj*sj; + } + + for (int j = 0; j < holder.m_NumberOfDependences; ++j) + { + double si = 0; + for (int i = 0; i < holder.m_NumberOfBins; ++i) + { + double sij = sijMatrix(i, j); + si += sij; + } + results.DependenceCountNonUniformity += si*si; + results.DependenceCountNonUniformityNormalised += si*si; + } + for (int i = 0; i < holder.m_NumberOfBins; ++i) + { + for (int j = 0; j < holder.m_NumberOfDependences; ++j) + { + double iInt = i + 1;// holder.IndexToMeanIntensity(i); + double sij = sijMatrix(i, j); + double k = j + 1; + double pij = sij / Ns; + + results.GreyLevelVariance += (iInt - results.MeanGreyLevelCount)* (iInt - results.MeanGreyLevelCount) * pij; + results.DependenceCountVariance += (k - results.MeanDependenceCount)* (k - results.MeanDependenceCount) * pij; + } + } + results.LowDependenceEmphasis /= Ns; + results.HighDependenceEmphasis /= Ns; + results.LowGreyLevelCountEmphasis /= Ns; + results.HighGreyLevelCountEmphasis /= Ns; + results.LowDependenceLowGreyLevelEmphasis /= Ns; + results.LowDependenceHighGreyLevelEmphasis /= Ns; + results.HighDependenceLowGreyLevelEmphasis /= Ns; + results.HighDependenceHighGreyLevelEmphasis /= Ns; + + results.GreyLevelNonUniformity /= Ns; + results.GreyLevelNonUniformityNormalised /= (Ns*Ns); + results.DependenceCountNonUniformity /= Ns; + results.DependenceCountNonUniformityNormalised /= (Ns*Ns); + + results.DependenceCountPercentage = 1; + + results.ExpectedNeighbourhoodSize = holder.m_NeighbourhoodSize; + results.AverageNeighbourhoodSize = holder.m_NumberOfNeighbourVoxels / (1.0 * holder.m_NumberOfNeighbourhoods); + results.AverageIncompleteNeighbourhoodSize = (holder.m_NumberOfNeighbourVoxels - holder.m_NumberOfCompleteNeighbourhoods* holder.m_NeighbourhoodSize) / (1.0 * (holder.m_NumberOfNeighbourhoods - holder.m_NumberOfCompleteNeighbourhoods)); + results.PercentageOfCompleteNeighbourhoods = (1.0*holder.m_NumberOfCompleteNeighbourhoods) / (1.0 * holder.m_NumberOfNeighbourhoods); + results.PercentageOfDependenceNeighbours = holder.m_NumberOfDependenceNeighbourVoxels / (1.0 * holder.m_NumberOfNeighbourVoxels); +} + +template +void +CalculateCoocurenceFeatures(itk::Image* itkImage, mitk::Image::Pointer mask, mitk::GIFNeighbouringGreyLevelDependenceFeature::FeatureListType & featureList, mitk::GIFNeighbouringGreyLevelDependenceFeature::GIFNeighbouringGreyLevelDependenceFeatureConfiguration config) +{ + typedef itk::Image MaskType; + + double rangeMin = config.MinimumIntensity; + double rangeMax = config.MaximumIntensity; + int numberOfBins = config.Bins; + + typename MaskType::Pointer maskImage = MaskType::New(); + mitk::CastToItkImage(mask, maskImage); + + std::vector resultVector; + int numberofDependency = 37; + if (VImageDimension == 2) + numberofDependency = 37; + + mitk::NGLDMMatrixHolder holderOverall(rangeMin, rangeMax, numberOfBins, numberofDependency); + mitk::NGLDMMatrixFeatures overallFeature; + CalculateNGLDMMatrix(itkImage, maskImage, config.alpha, config.range, config.direction, holderOverall); + LocalCalculateFeatures(holderOverall, overallFeature); + + MatrixFeaturesTo(overallFeature, config.FeatureEncoding, featureList); +} + + +static +void MatrixFeaturesTo(mitk::NGLDMMatrixFeatures features, + std::string prefix, + mitk::GIFNeighbouringGreyLevelDependenceFeature::FeatureListType &featureList) +{ + featureList.push_back(std::make_pair(prefix + "Low Dependence Emphasis", features.LowDependenceEmphasis)); + featureList.push_back(std::make_pair(prefix + "High Dependence Emphasis", features.HighDependenceEmphasis)); + featureList.push_back(std::make_pair(prefix + "Low Grey Level Count Emphasis", features.LowGreyLevelCountEmphasis)); + featureList.push_back(std::make_pair(prefix + "High Grey Level Count Emphasis", features.HighGreyLevelCountEmphasis)); + featureList.push_back(std::make_pair(prefix + "Low Dependence Low Grey Level Emphasis", features.LowDependenceLowGreyLevelEmphasis)); + featureList.push_back(std::make_pair(prefix + "Low Dependence High Grey Level Emphasis", features.LowDependenceHighGreyLevelEmphasis)); + featureList.push_back(std::make_pair(prefix + "High Dependence Low Grey Level Emphasis", features.HighDependenceLowGreyLevelEmphasis)); + featureList.push_back(std::make_pair(prefix + "High Dependence High Grey Level Emphasis", features.HighDependenceHighGreyLevelEmphasis)); + + featureList.push_back(std::make_pair(prefix + "Grey Level Non-Uniformity", features.GreyLevelNonUniformity)); + featureList.push_back(std::make_pair(prefix + "Grey Level Non-Uniformity Normalised", features.GreyLevelNonUniformityNormalised)); + featureList.push_back(std::make_pair(prefix + "Dependence Count Non-Uniformity", features.DependenceCountNonUniformity)); + featureList.push_back(std::make_pair(prefix + "Dependence Count Non-Uniformtiy Normalised", features.DependenceCountNonUniformityNormalised)); + + featureList.push_back(std::make_pair(prefix + "Dependence Count Percentage", features.DependenceCountPercentage)); + featureList.push_back(std::make_pair(prefix + "Grey Level Mean", features.MeanGreyLevelCount)); + featureList.push_back(std::make_pair(prefix + "Grey Level Variance", features.GreyLevelVariance)); + featureList.push_back(std::make_pair(prefix + "Dependence Count Mean", features.MeanDependenceCount)); + featureList.push_back(std::make_pair(prefix + "Dependence Count Variance", features.DependenceCountVariance)); + featureList.push_back(std::make_pair(prefix + "Dependence Count Entropy", features.DependenceCountEntropy)); + featureList.push_back(std::make_pair(prefix + "Dependence Count Energy", features.DependenceCountEnergy)); + + featureList.push_back(std::make_pair(prefix + "Expected Neighbourhood Size", features.ExpectedNeighbourhoodSize)); + featureList.push_back(std::make_pair(prefix + "Average Neighbourhood Size", features.AverageNeighbourhoodSize)); + featureList.push_back(std::make_pair(prefix + "Average Incomplete Neighbourhood Size", features.AverageIncompleteNeighbourhoodSize)); + featureList.push_back(std::make_pair(prefix + "Percentage of complete Neighbourhoods", features.PercentageOfCompleteNeighbourhoods)); + featureList.push_back(std::make_pair(prefix + "Percentage of Dependence Neighbour Voxels", features.PercentageOfDependenceNeighbours)); + +} + +mitk::GIFNeighbouringGreyLevelDependenceFeature::GIFNeighbouringGreyLevelDependenceFeature() : +m_Range(1.0) +{ + SetShortName("ngld"); + SetLongName("neighbouring-grey-level-dependence"); + SetFeatureClassName("Neighbouring Grey Level Dependence"); +} + +mitk::GIFNeighbouringGreyLevelDependenceFeature::FeatureListType mitk::GIFNeighbouringGreyLevelDependenceFeature::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) +{ + FeatureListType featureList; + InitializeQuantifier(image, mask); + + GIFNeighbouringGreyLevelDependenceFeatureConfiguration config; + config.direction = GetDirection(); + config.range = m_Range; + config.alpha = 0; + + config.MinimumIntensity = GetQuantifier()->GetMinimum(); + config.MaximumIntensity = GetQuantifier()->GetMaximum(); + config.Bins = GetQuantifier()->GetBins(); + + config.FeatureEncoding = FeatureDescriptionPrefix(); + + AccessByItk_3(image, CalculateCoocurenceFeatures, mask, featureList,config); + + return featureList; +} + +mitk::GIFNeighbouringGreyLevelDependenceFeature::FeatureNameListType mitk::GIFNeighbouringGreyLevelDependenceFeature::GetFeatureNames() +{ + FeatureNameListType featureList; + + return featureList; +} + + + +void mitk::GIFNeighbouringGreyLevelDependenceFeature::AddArguments(mitkCommandLineParser &parser) +{ + std::string name = GetOptionPrefix(); + + parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Calculate Neighbouring Grey Level Dependence Features", "Calculate Neighbouring grey level dependence based features", us::Any()); + parser.addArgument(name + "::range", name + "::range", mitkCommandLineParser::String, "NGLD Range", "Define the range that is used (Semicolon-separated)", us::Any()); + + AddQuantifierArguments(parser); +} + +void +mitk::GIFNeighbouringGreyLevelDependenceFeature::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &, const Image::Pointer &maskNoNAN, FeatureListType &featureList) +{ + auto parsedArgs = GetParameter(); + std::string name = GetOptionPrefix(); + + if (parsedArgs.count(GetLongName())) + { + std::vector ranges; + if (parsedArgs.count(name + "::range")) + { + ranges = SplitDouble(parsedArgs[name + "::range"].ToString(), ';'); + } + else + { + ranges.push_back(1); + } + for (double range : ranges) + { + InitializeQuantifierFromParameters(feature, maskNoNAN); + this->SetRange(range); + MITK_INFO << "Start calculating NGLD"; + auto localResults = this->CalculateFeatures(feature, maskNoNAN); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating NGLD"; + } + } +} + +std::string mitk::GIFNeighbouringGreyLevelDependenceFeature::GetCurrentFeatureEncoding() +{ + std::ostringstream ss; + ss << m_Range; + std::string strRange = ss.str(); + return QuantifierParameterString() + "_Range-"+ss.str(); +} + diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricDensityStatistics.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricDensityStatistics.cpp new file mode 100644 index 0000000000..0b74fe0845 --- /dev/null +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricDensityStatistics.cpp @@ -0,0 +1,526 @@ +/*=================================================================== + +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 + +// MITK +#include +#include +#include +#include +#include + +// ITK +#include +#include +#include +#include + +// VTK +#include +#include +#include +#include +#include +#include +#include +#include + +// STL +#include +#include + +// Eigen +#include + +struct GIFVolumetricDensityStatisticsParameters +{ + double volume; + std::string prefix; +}; + +template +void +CalculateVolumeDensityStatistic(itk::Image* itkImage, mitk::Image::Pointer mask, GIFVolumetricDensityStatisticsParameters params, mitk::GIFVolumetricDensityStatistics::FeatureListType & featureList) +{ + typedef itk::Image ImageType; + typedef itk::Image MaskType; + + double volume = params.volume; + std::string prefix = params.prefix; + + typename MaskType::Pointer maskImage = MaskType::New(); + mitk::CastToItkImage(mask, maskImage); + + itk::ImageRegionConstIteratorWithIndex imgA(itkImage, itkImage->GetLargestPossibleRegion()); + itk::ImageRegionConstIteratorWithIndex imgB(itkImage, itkImage->GetLargestPossibleRegion()); + itk::ImageRegionConstIteratorWithIndex maskA(maskImage, maskImage->GetLargestPossibleRegion()); + itk::ImageRegionConstIteratorWithIndex maskB(maskImage, maskImage->GetLargestPossibleRegion()); + + double moranA = 0; + double moranB = 0; + double geary = 0; + double Nv = 0; + double w_ij = 0; + double mean = 0; + + typename ImageType::PointType pointA; + typename ImageType::PointType pointB; + + while (!imgA.IsAtEnd()) + { + if (maskA.Get() > 0) + { + Nv += 1; + mean += imgA.Get(); + } + ++imgA; + ++maskA; + } + mean /= Nv; + imgA.GoToBegin(); + maskA.GoToBegin(); + while (!imgA.IsAtEnd()) + { + if (maskA.Get() > 0) + { + imgB.GoToBegin(); + maskB.GoToBegin(); + while (!imgB.IsAtEnd()) + { + if ((imgA.GetIndex() == imgB.GetIndex()) || + (maskB.Get() < 1)) + { + ++imgB; + ++maskB; + continue; + } + itkImage->TransformIndexToPhysicalPoint(maskA.GetIndex(), pointA); + itkImage->TransformIndexToPhysicalPoint(maskB.GetIndex(), pointB); + + double w = 1 / pointA.EuclideanDistanceTo(pointB); + moranA += w*(imgA.Get() - mean)* (imgB.Get() - mean); + geary += w * (imgA.Get() - imgB.Get()) * (imgA.Get() - imgB.Get()); + + w_ij += w; + + ++imgB; + ++maskB; + } + moranB += (imgA.Get() - mean)* (imgA.Get() - mean); + } + ++imgA; + ++maskA; + } + + featureList.push_back(std::make_pair(prefix + "Volume integrated intensity", volume* mean)); + featureList.push_back(std::make_pair(prefix + "Volume Moran's I index", Nv / w_ij * moranA / moranB)); + featureList.push_back(std::make_pair(prefix + "Volume Geary's C measure", ( Nv -1 ) / 2 / w_ij * geary/ moranB)); +} + +void calculateMOBB(vtkPointSet *pointset, double &volume, double &surface) +{ + volume = std::numeric_limits::max(); + + for (int cellID = 0; cellID < pointset->GetNumberOfCells(); ++cellID) + { + auto cell = pointset->GetCell(cellID); + + for (int edgeID = 0; edgeID < 3; ++edgeID) + { + auto edge = cell->GetEdge(edgeID); + + double pA[3], pB[3]; + double pAA[3], pBB[3]; + + vtkSmartPointer transform = vtkSmartPointer::New(); + transform->PostMultiply(); + pointset->GetPoint(edge->GetPointId(0), pA); + pointset->GetPoint(edge->GetPointId(1), pB); + + double angleZ = std::atan2((- pA[2] + pB[2]) ,(pA[1] - pB[1])); + angleZ *= 180 / vnl_math::pi; + if (pA[2] == pB[2]) + angleZ = 0; + + transform->RotateX(angleZ); + transform->TransformPoint(pA, pAA); + transform->TransformPoint(pB, pBB); + + double angleY = std::atan2((pAA[1] -pBB[1]) ,-(pAA[0] - pBB[0])); + angleY *= 180 / vnl_math::pi; + if (pAA[1] == pBB[1]) + angleY = 0; + transform->RotateZ(angleY); + + double p0[3]; + pointset->GetPoint(edge->GetPointId(0), p0); + + double curMinX = std::numeric_limits::max(); + double curMaxX = std::numeric_limits::lowest(); + double curMinY = std::numeric_limits::max(); + double curMaxY = std::numeric_limits::lowest(); + double curMinZ = std::numeric_limits::max(); + double curMaxZ = std::numeric_limits::lowest(); + for (int pointID = 0; pointID < pointset->GetNumberOfPoints(); ++pointID) + { + double p[3]; + double p2[3]; + pointset->GetPoint(pointID, p); + p[0] -= p0[0]; p[1] -= p0[1]; p[2] -= p0[2]; + transform->TransformPoint(p, p2); + + curMinX = std::min(p2[0], curMinX); + curMaxX = std::max(p2[0], curMaxX); + curMinY = std::min(p2[1], curMinY); + curMaxY = std::max(p2[1], curMaxY); + curMinZ = std::min(p2[2], curMinZ); + curMaxZ = std::max(p2[2], curMaxZ); + + //std::cout << pointID << " (" << p[0] << "|" << p[1] << "|" << p[2] << ") (" << p2[0] << "|" << p2[1] << "|" << p2[2] << ")" << std::endl; + } + + if ((curMaxX - curMinX)*(curMaxY - curMinY)*(curMaxZ - curMinZ) < volume) + { + volume = (curMaxX - curMinX)*(curMaxY - curMinY)*(curMaxZ - curMinZ); + surface = (curMaxX - curMinX)*(curMaxX - curMinX) + (curMaxY - curMinY)*(curMaxY - curMinY) + (curMaxZ - curMinZ)*(curMaxZ - curMinZ); + surface *= 2; + } + + + } + } +} + +void calculateMEE(vtkPointSet *pointset, double &vol, double &surf, double tolerance=0.0000001) +{ + // Inspired by https://github.com/smdabdoub/ProkaryMetrics/blob/master/calc/fitting.py + + int numberOfPoints = pointset->GetNumberOfPoints(); + int dimension = 3; + Eigen::MatrixXd points(3, numberOfPoints); + Eigen::MatrixXd Q(3+1, numberOfPoints); + double p[3]; + for (int i = 0; i < numberOfPoints; ++i) + { + pointset->GetPoint(i, p); + points(0, i) = p[0]; + points(1, i) = p[1]; + points(2, i) = p[2]; + Q(0, i) = p[0]; + Q(1, i) = p[1]; + Q(2, i) = p[2]; + Q(3, i) = p[3]; + } + + int count = 1; + double error = 1; + Eigen::VectorXd u_vector(numberOfPoints); + u_vector.fill(1.0 / numberOfPoints); + Eigen::DiagonalMatrix u = u_vector.asDiagonal(); + Eigen::VectorXd ones(dimension + 1); + ones.fill(1); + Eigen::MatrixXd Ones = ones.asDiagonal(); + + // Khachiyan Algorithm + while (error > tolerance) + { + auto Qt = Q.transpose(); + Eigen::MatrixXd X = Q*u*Qt; + Eigen::FullPivHouseholderQR qr(X); + Eigen::MatrixXd Xi = qr.solve(Ones); + Eigen::MatrixXd M = Qt * Xi * Q; + + double maximumValue = M(0, 0); + int maximumPosition = 0; + for (int i = 0; i < numberOfPoints; ++i) + { + if (maximumValue < M(i, i)) + { + maximumValue = M(i, i); + maximumPosition = i; + } + } + double stepsize = (maximumValue - dimension - 1) / ((dimension + 1) * (maximumValue - 1)); + Eigen::DiagonalMatrix new_u = (1.0 - stepsize) * u; + new_u.diagonal()[maximumPosition] = (new_u.diagonal())(maximumPosition) + stepsize; + ++count; + error = (new_u.diagonal() - u.diagonal()).norm(); + u.diagonal() = new_u.diagonal(); + } + + // U = u + Eigen::MatrixXd Ai = points * u * points.transpose() - points * u *(points * u).transpose(); + Eigen::FullPivHouseholderQR qr(Ai); + Eigen::VectorXd ones2(dimension); + ones2.fill(1); + Eigen::MatrixXd Ones2 = ones2.asDiagonal(); + Eigen::MatrixXd A = qr.solve(Ones2)*1.0/dimension; + + Eigen::JacobiSVD svd(A); + double c = 1 / sqrt(svd.singularValues()[0]); + double b = 1 / sqrt(svd.singularValues()[1]); + double a = 1 / sqrt(svd.singularValues()[2]); + double V = 4 * vnl_math::pi*a*b*c / 3; + + double ad_mvee= 0; + double alpha = std::sqrt(1 - b*b / a / a); + double beta = std::sqrt(1 - c*c / a / a); + for (int i = 0; i < 20; ++i) + { + ad_mvee += 4 * vnl_math::pi*a*b*(alpha*alpha + beta*beta) / (2 * alpha*beta) * (std::pow(alpha*beta, i)) / (1 - 4 * i*i); + } + vol = V; + surf = ad_mvee; +} + +mitk::GIFVolumetricDensityStatistics::FeatureListType mitk::GIFVolumetricDensityStatistics::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) +{ + FeatureListType featureList; + if (image->GetDimension() < 3) + { + return featureList; + } + + std::string prefix = FeatureDescriptionPrefix(); + + vtkSmartPointer mesher = vtkSmartPointer::New(); + vtkSmartPointer stats = vtkSmartPointer::New(); + vtkSmartPointer stats2 = vtkSmartPointer::New(); + mesher->SetInputData(mask->GetVtkImageData()); + mesher->SetValue(0, 0.5); + stats->SetInputConnection(mesher->GetOutputPort()); + stats->Update(); + + vtkSmartPointer delaunay = + vtkSmartPointer< vtkDelaunay3D >::New(); + delaunay->SetInputConnection(mesher->GetOutputPort()); + delaunay->SetAlpha(0); + delaunay->Update(); + vtkSmartPointer geometryFilter = + vtkSmartPointer::New(); + geometryFilter->SetInputConnection(delaunay->GetOutputPort()); + geometryFilter->Update(); + stats2->SetInputConnection(geometryFilter->GetOutputPort()); + stats2->Update(); + + double vol_mvee; + double surf_mvee; + calculateMEE(mesher->GetOutput(), vol_mvee, surf_mvee); + + double vol_mobb; + double surf_mobb; + calculateMOBB(geometryFilter->GetOutput(), vol_mobb, surf_mobb); + + double pi = vnl_math::pi; + + double meshVolume = stats->GetVolume(); + double meshSurf = stats->GetSurfaceArea(); + + GIFVolumetricDensityStatisticsParameters params; + params.volume = meshVolume; + params.prefix = prefix; + AccessByItk_3(image, CalculateVolumeDensityStatistic, mask, params, featureList); + + //Calculate center of mass shift + int xx = mask->GetDimensions()[0]; + int yy = mask->GetDimensions()[1]; + int zz = mask->GetDimensions()[2]; + + double xd = mask->GetGeometry()->GetSpacing()[0]; + double yd = mask->GetGeometry()->GetSpacing()[1]; + double zd = mask->GetGeometry()->GetSpacing()[2]; + + int minimumX=xx; + int maximumX=0; + int minimumY=yy; + int maximumY=0; + int minimumZ=zz; + int maximumZ=0; + + vtkSmartPointer dataset1Arr = vtkSmartPointer::New(); + vtkSmartPointer dataset2Arr = vtkSmartPointer::New(); + vtkSmartPointer dataset3Arr = vtkSmartPointer::New(); + dataset1Arr->SetNumberOfComponents(1); + dataset2Arr->SetNumberOfComponents(1); + dataset3Arr->SetNumberOfComponents(1); + dataset1Arr->SetName("M1"); + dataset2Arr->SetName("M2"); + dataset3Arr->SetName("M3"); + + vtkSmartPointer dataset1ArrU = vtkSmartPointer::New(); + vtkSmartPointer dataset2ArrU = vtkSmartPointer::New(); + vtkSmartPointer dataset3ArrU = vtkSmartPointer::New(); + dataset1ArrU->SetNumberOfComponents(1); + dataset2ArrU->SetNumberOfComponents(1); + dataset3ArrU->SetNumberOfComponents(1); + dataset1ArrU->SetName("M1"); + dataset2ArrU->SetName("M2"); + dataset3ArrU->SetName("M3"); + + vtkSmartPointer points = + vtkSmartPointer< vtkPoints >::New(); + + for (int x = 0; x < xx; x++) + { + for (int y = 0; y < yy; y++) + { + for (int z = 0; z < zz; z++) + { + itk::Image::IndexType index; + + index[0] = x; + index[1] = y; + index[2] = z; + + mitk::ScalarType pxImage; + mitk::ScalarType pxMask; + + mitkPixelTypeMultiplex5( + mitk::FastSinglePixelAccess, + image->GetChannelDescriptor().GetPixelType(), + image, + image->GetVolumeData(), + index, + pxImage, + 0); + + mitkPixelTypeMultiplex5( + mitk::FastSinglePixelAccess, + mask->GetChannelDescriptor().GetPixelType(), + mask, + mask->GetVolumeData(), + index, + pxMask, + 0); + + //Check if voxel is contained in segmentation + if (pxMask > 0) + { + minimumX = std::min(x, minimumX); + minimumY = std::min(y, minimumY); + minimumZ = std::min(z, minimumZ); + maximumX = std::max(x, maximumX); + maximumY = std::max(y, maximumY); + maximumZ = std::max(z, maximumZ); + points->InsertNextPoint(x*xd, y*yd, z*zd); + + if (pxImage == pxImage) + { + dataset1Arr->InsertNextValue(x*xd); + dataset2Arr->InsertNextValue(y*yd); + dataset3Arr->InsertNextValue(z*zd); + } + } + } + } + } + + vtkSmartPointer datasetTable = vtkSmartPointer::New(); + datasetTable->AddColumn(dataset1Arr); + datasetTable->AddColumn(dataset2Arr); + datasetTable->AddColumn(dataset3Arr); + + vtkSmartPointer pcaStatistics = vtkSmartPointer::New(); + pcaStatistics->SetInputData(vtkStatisticsAlgorithm::INPUT_DATA, datasetTable); + pcaStatistics->SetColumnStatus("M1", 1); + pcaStatistics->SetColumnStatus("M2", 1); + pcaStatistics->SetColumnStatus("M3", 1); + pcaStatistics->RequestSelectedColumns(); + pcaStatistics->SetDeriveOption(true); + pcaStatistics->Update(); + + vtkSmartPointer eigenvalues = vtkSmartPointer::New(); + pcaStatistics->GetEigenvalues(eigenvalues); + + std::vector eigen_val(3); + eigen_val[2] = eigenvalues->GetValue(0); + eigen_val[1] = eigenvalues->GetValue(1); + eigen_val[0] = eigenvalues->GetValue(2); + + double major = 2*sqrt(eigen_val[2]); + double minor = 2*sqrt(eigen_val[1]); + double least = 2*sqrt(eigen_val[0]); + + double alpha = std::sqrt(1 - minor*minor / major / major); + double beta = std::sqrt(1 - least*least / major / major); + + double a = (maximumX - minimumX+1) * xd; + double b = (maximumY - minimumY+1) * yd; + double c = (maximumZ - minimumZ+1) * zd; + + double vd_aabb = meshVolume / (a*b*c); + double ad_aabb = meshSurf / (2 * a*b + 2 * a*c + 2 * b*c); + + double vd_aee = 3 * meshVolume / (4.0*pi*major*minor*least); + double ad_aee = 0; + for (int i = 0; i < 20; ++i) + { + ad_aee += 4 * pi*major*minor*(alpha*alpha + beta*beta) / (2 * alpha*beta) * (std::pow(alpha*beta, i)) / (1 - 4 * i*i); + } + ad_aee = meshSurf / ad_aee; + + double vd_ch = meshVolume / stats2->GetVolume(); + double ad_ch = meshSurf / stats2->GetSurfaceArea(); + + featureList.push_back(std::make_pair(prefix + "Volume density axis-aligned bounding box", vd_aabb)); + featureList.push_back(std::make_pair(prefix + "Surface density axis-aligned bounding box", ad_aabb)); + featureList.push_back(std::make_pair(prefix + "Volume density oriented minimum bounding box", meshVolume / vol_mobb)); + featureList.push_back(std::make_pair(prefix + "Surface density oriented minimum bounding box", meshSurf / surf_mobb)); + featureList.push_back(std::make_pair(prefix + "Volume density approx. enclosing ellipsoid", vd_aee)); + featureList.push_back(std::make_pair(prefix + "Surface density approx. enclosing ellipsoid", ad_aee)); + featureList.push_back(std::make_pair(prefix + "Volume density approx. minimum volume enclosing ellipsoid", meshVolume / vol_mvee)); + featureList.push_back(std::make_pair(prefix + "Surface density approx. minimum volume enclosing ellipsoid", meshSurf / surf_mvee)); + featureList.push_back(std::make_pair(prefix + "Volume density convex hull", vd_ch)); + featureList.push_back(std::make_pair(prefix + "Surface density convex hull", ad_ch)); + + return featureList; +} + +mitk::GIFVolumetricDensityStatistics::GIFVolumetricDensityStatistics() +{ + SetLongName("volume-density"); + SetShortName("volden"); + SetFeatureClassName("Morphological Density"); +} + +mitk::GIFVolumetricDensityStatistics::FeatureNameListType mitk::GIFVolumetricDensityStatistics::GetFeatureNames() +{ + FeatureNameListType featureList; + return featureList; +} + + +void mitk::GIFVolumetricDensityStatistics::AddArguments(mitkCommandLineParser &parser) +{ + std::string name = GetOptionPrefix(); + + parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Volume-Density Statistic", "calculates volume density based features", us::Any()); +} + +void +mitk::GIFVolumetricDensityStatistics::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &, FeatureListType &featureList) +{ + auto parsedArgs = GetParameter(); + if (parsedArgs.count(GetLongName())) + { + MITK_INFO << "Start calculating volumetric density features ...."; + auto localResults = this->CalculateFeatures(feature, mask); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating volumetric density features...."; + } +} + diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricStatistics.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricStatistics.cpp index 662480ae8c..89f4cc8da8 100644 --- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricStatistics.cpp +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricStatistics.cpp @@ -1,170 +1,460 @@ /*=================================================================== 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 // MITK #include #include #include +#include +#include // ITK #include #include // VTK #include #include #include +#include +#include +#include // STL -#include #include template void - CalculateVolumeStatistic(itk::Image* itkImage, mitk::Image::Pointer mask, mitk::GIFVolumetricStatistics::FeatureListType & featureList) + CalculateVolumeStatistic(itk::Image* itkImage, mitk::Image::Pointer mask, mitk::GIFVolumetricStatistics::FeatureListType & featureList, std::string prefix) { typedef itk::Image ImageType; typedef itk::Image MaskType; typedef itk::LabelStatisticsImageFilter FilterType; typename MaskType::Pointer maskImage = MaskType::New(); mitk::CastToItkImage(mask, maskImage); typename FilterType::Pointer labelStatisticsImageFilter = FilterType::New(); labelStatisticsImageFilter->SetInput( itkImage ); labelStatisticsImageFilter->SetLabelInput(maskImage); labelStatisticsImageFilter->Update(); double volume = labelStatisticsImageFilter->GetCount(1); + double voxelVolume = 1; for (int i = 0; i < (int)(VImageDimension); ++i) { volume *= itkImage->GetSpacing()[i]; + voxelVolume *= itkImage->GetSpacing()[i]; } - featureList.push_back(std::make_pair("Volumetric Features Volume (pixel based)",volume)); + featureList.push_back(std::make_pair(prefix + "Voxel Volume", voxelVolume)); + featureList.push_back(std::make_pair(prefix + "Volume (voxel based)", volume)); } template void - CalculateLargestDiameter(itk::Image* mask, mitk::GIFVolumetricStatistics::FeatureListType & featureList) + CalculateLargestDiameter(itk::Image* mask, mitk::Image::Pointer valueImage, mitk::GIFVolumetricStatistics::FeatureListType & featureList, std::string prefix) { + typedef itk::Image ValueImageType; + + typename ValueImageType::Pointer itkValueImage = ValueImageType::New(); + mitk::CastToItkImage(valueImage, itkValueImage); + typedef itk::Image ImageType; typedef typename ImageType::PointType PointType; typename ImageType::SizeType radius; for (int i=0; i < (int)VImageDimension; ++i) radius[i] = 1; - itk::NeighborhoodIterator iterator(radius,mask, mask->GetRequestedRegion()); + itk::NeighborhoodIterator iterator(radius, mask, mask->GetRequestedRegion()); + itk::NeighborhoodIterator valueIter(radius, itkValueImage, itkValueImage->GetRequestedRegion()); std::vector borderPoints; + + unsigned int maskDimensionX = mask->GetLargestPossibleRegion().GetSize()[0]; + unsigned int maskDimensionY = mask->GetLargestPossibleRegion().GetSize()[1]; + unsigned int maskDimensionZ = mask->GetLargestPossibleRegion().GetSize()[2]; + + double maskVoxelSpacingX = mask->GetSpacing()[0]; + double maskVoxelSpacingY = mask->GetSpacing()[1]; + double maskVoxelSpacingZ = mask->GetSpacing()[2]; + + unsigned int maskMinimumX = maskDimensionX; + unsigned int maskMaximumX = 0; + unsigned int maskMinimumY = maskDimensionY; + unsigned int maskMaximumY = 0; + unsigned int maskMinimumZ = maskDimensionZ; + unsigned int maskMaximumZ = 0; + + // + // Calculate surface in different directions + // + double surface = 0; + std::vector directionSurface; + for (int i = 0; i < (int)(iterator.Size()); ++i) + { + auto offset = iterator.GetOffset(i); + double deltaS = 1; + int nonZeros = 0; + for (unsigned int j = 0; j < VImageDimension; ++j) + { + if (offset[j] != 0 && nonZeros == 0) + { + for (unsigned int k = 0; k < VImageDimension; ++k) + { + if (k != j) + deltaS *= mask->GetSpacing()[k]; + } + nonZeros++; + } + else if (offset[j] != 0) + { + deltaS = 0; + } + } + if (nonZeros < 1) + deltaS = 0; + directionSurface.push_back(deltaS); + } + + // + // Prepare calulation of Centre of mass shift + // + PointType normalCenter(0); + PointType normalCenterUncorrected(0); + PointType weightedCenter(0); + PointType currentPoint; + int numberOfPoints = 0; + int numberOfPointsUncorrected = 0; + double sumOfPoints = 0; + while(!iterator.IsAtEnd()) { if (iterator.GetCenterPixel() == 0) { ++iterator; + ++valueIter; continue; } + maskMinimumX = (maskMinimumX > iterator.GetIndex()[0]) ? iterator.GetIndex()[0] : maskMinimumX; + maskMaximumX = (maskMaximumX < iterator.GetIndex()[0]) ? iterator.GetIndex()[0] : maskMaximumX; + maskMinimumY = (maskMinimumY > iterator.GetIndex()[1]) ? iterator.GetIndex()[1] : maskMinimumY; + maskMaximumY = (maskMaximumY < iterator.GetIndex()[1]) ? iterator.GetIndex()[1] : maskMaximumY; + maskMinimumZ = (maskMinimumZ > iterator.GetIndex()[2]) ? iterator.GetIndex()[2] : maskMinimumZ; + maskMaximumZ = (maskMaximumZ < iterator.GetIndex()[2]) ? iterator.GetIndex()[2] : maskMaximumZ; + + mask->TransformIndexToPhysicalPoint(iterator.GetIndex(), currentPoint); + + normalCenterUncorrected += currentPoint.GetVectorFromOrigin(); + ++numberOfPointsUncorrected; + + double intensityValue = valueIter.GetCenterPixel(); + if (intensityValue == intensityValue) + { + normalCenter += currentPoint.GetVectorFromOrigin(); + weightedCenter += currentPoint.GetVectorFromOrigin() * intensityValue; + sumOfPoints += intensityValue; + ++numberOfPoints; + } + bool border = false; for (int i = 0; i < (int)(iterator.Size()); ++i) { - if (iterator.GetPixel(i) == 0) + if (iterator.GetPixel(i) == 0 || ( ! iterator.IndexInBounds(i))) { border = true; - break; + surface += directionSurface[i]; + //break; } } if (border) { auto centerIndex = iterator.GetIndex(); PointType centerPoint; mask->TransformIndexToPhysicalPoint(centerIndex, centerPoint ); borderPoints.push_back(centerPoint); } ++iterator; + ++valueIter; } + auto normalCenterVector = normalCenter.GetVectorFromOrigin() / numberOfPoints; + auto normalCenterVectorUncorrected = normalCenter.GetVectorFromOrigin() / numberOfPointsUncorrected; + auto weightedCenterVector = weightedCenter.GetVectorFromOrigin() / sumOfPoints; + auto differenceOfCentersUncorrected = (normalCenterVectorUncorrected - weightedCenterVector).GetNorm(); + auto differenceOfCenters = (normalCenterVector - weightedCenterVector).GetNorm(); + double longestDiameter = 0; unsigned long numberOfBorderPoints = borderPoints.size(); for (int i = 0; i < (int)numberOfBorderPoints; ++i) { auto point = borderPoints[i]; for (int j = i; j < (int)numberOfBorderPoints; ++j) { double newDiameter=point.EuclideanDistanceTo(borderPoints[j]); if (newDiameter > longestDiameter) longestDiameter = newDiameter; } } - featureList.push_back(std::make_pair("Volumetric Features Maximum 3D diameter",longestDiameter)); + double boundingBoxVolume = maskVoxelSpacingX* (maskMaximumX - maskMinimumX) * maskVoxelSpacingY* (maskMaximumY - maskMinimumY) * maskVoxelSpacingZ* (maskMaximumZ - maskMinimumZ); + + featureList.push_back(std::make_pair(prefix + "Maximum 3D diameter", longestDiameter)); + featureList.push_back(std::make_pair(prefix + "Surface (voxel based)", surface)); + featureList.push_back(std::make_pair(prefix + "Centre of mass shift", differenceOfCenters)); + featureList.push_back(std::make_pair(prefix + "Centre of mass shift (uncorrected)", differenceOfCentersUncorrected)); + featureList.push_back(std::make_pair(prefix + "Bounding Box Volume", boundingBoxVolume)); } mitk::GIFVolumetricStatistics::GIFVolumetricStatistics() { + SetLongName("volume"); + SetShortName("vol"); + SetFeatureClassName("Volumetric Features"); } mitk::GIFVolumetricStatistics::FeatureListType mitk::GIFVolumetricStatistics::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) { FeatureListType featureList; + if (image->GetDimension() < 3) + { + return featureList; + } + - AccessByItk_2(image, CalculateVolumeStatistic, mask, featureList); - AccessByItk_1(mask, CalculateLargestDiameter, featureList); + AccessByItk_3(image, CalculateVolumeStatistic, mask, featureList, FeatureDescriptionPrefix()); + AccessByItk_3(mask, CalculateLargestDiameter, image, featureList, FeatureDescriptionPrefix()); vtkSmartPointer mesher = vtkSmartPointer::New(); vtkSmartPointer stats = vtkSmartPointer::New(); mesher->SetInputData(mask->GetVtkImageData()); + mesher->SetValue(0, 0.5); stats->SetInputConnection(mesher->GetOutputPort()); stats->Update(); double pi = vnl_math::pi; double meshVolume = stats->GetVolume(); double meshSurf = stats->GetSurfaceArea(); double pixelVolume = featureList[0].second; + double pixelSurface = featureList[3].second; + + MITK_INFO << "Surface: " << pixelSurface << " Volume: " << pixelVolume; + + double compactness1 = pixelVolume / (std::sqrt(pi) * std::pow(meshSurf, 2.0 / 3.0)); + double compactness1Pixel = pixelVolume / (std::sqrt(pi) * std::pow(pixelSurface, 2.0 / 3.0)); + //This is the definition used by Aertz. However, due to 2/3 this feature is not demensionless. Use compactness3 instead. - double compactness1 = pixelVolume / ( std::sqrt(pi) * std::pow(meshSurf, 2.0/3.0)); - double compactness2 = 36*pi*pixelVolume*pixelVolume/meshSurf/meshSurf/meshSurf; + double compactness2 = 36 * pi*pixelVolume*pixelVolume / meshSurf / meshSurf / meshSurf; + double compactness2Pixel = 36 * pi*pixelVolume*pixelVolume / pixelSurface / pixelSurface / pixelSurface; + double compactness3 = pixelVolume / (std::sqrt(pi) * std::pow(meshSurf, 3.0 / 2.0)); + double compactness3Pixel = pixelVolume / (std::sqrt(pi) * std::pow(pixelSurface, 3.0 / 2.0)); - double sphericity=std::pow(pi,1/3.0) *std::pow(6*pixelVolume, 2.0/3.0) / meshSurf; + double sphericity = std::pow(pi, 1 / 3.0) *std::pow(6 * pixelVolume, 2.0 / 3.0) / meshSurf; + double sphericityPixel = std::pow(pi, 1 / 3.0) *std::pow(6 * pixelVolume, 2.0 / 3.0) / pixelSurface; double surfaceToVolume = meshSurf / pixelVolume; + double surfaceToVolumePixel = pixelSurface / pixelVolume; double sphericalDisproportion = meshSurf / 4 / pi / std::pow(3.0 / 4.0 / pi * pixelVolume, 2.0 / 3.0); + double sphericalDisproportionPixel = pixelSurface / 4 / pi / std::pow(3.0 / 4.0 / pi * pixelVolume, 2.0 / 3.0); + double asphericity = std::pow(1.0/compactness2, (1.0 / 3.0)) - 1; + double asphericityPixel = std::pow(1.0/compactness2Pixel, (1.0 / 3.0)) - 1; - featureList.push_back(std::make_pair("Volumetric Features Volume (mesh based)",meshVolume)); - featureList.push_back(std::make_pair("Volumetric Features Surface area",meshSurf)); - featureList.push_back(std::make_pair("Volumetric Features Surface to volume ratio",surfaceToVolume)); - featureList.push_back(std::make_pair("Volumetric Features Sphericity",sphericity)); - featureList.push_back(std::make_pair("Volumetric Features Compactness 1",compactness1)); - featureList.push_back(std::make_pair("Volumetric Features Compactness 2",compactness2)); - featureList.push_back(std::make_pair("Volumetric Features Spherical disproportion",sphericalDisproportion)); + //Calculate center of mass shift + int xx = mask->GetDimensions()[0]; + int yy = mask->GetDimensions()[1]; + int zz = mask->GetDimensions()[2]; + + double xd = mask->GetGeometry()->GetSpacing()[0]; + double yd = mask->GetGeometry()->GetSpacing()[1]; + double zd = mask->GetGeometry()->GetSpacing()[2]; + + vtkSmartPointer dataset1Arr = vtkSmartPointer::New(); + vtkSmartPointer dataset2Arr = vtkSmartPointer::New(); + vtkSmartPointer dataset3Arr = vtkSmartPointer::New(); + dataset1Arr->SetNumberOfComponents(1); + dataset2Arr->SetNumberOfComponents(1); + dataset3Arr->SetNumberOfComponents(1); + dataset1Arr->SetName("M1"); + dataset2Arr->SetName("M2"); + dataset3Arr->SetName("M3"); + + vtkSmartPointer dataset1ArrU = vtkSmartPointer::New(); + vtkSmartPointer dataset2ArrU = vtkSmartPointer::New(); + vtkSmartPointer dataset3ArrU = vtkSmartPointer::New(); + dataset1ArrU->SetNumberOfComponents(1); + dataset2ArrU->SetNumberOfComponents(1); + dataset3ArrU->SetNumberOfComponents(1); + dataset1ArrU->SetName("M1"); + dataset2ArrU->SetName("M2"); + dataset3ArrU->SetName("M3"); + + for (int x = 0; x < xx; x++) + { + for (int y = 0; y < yy; y++) + { + for (int z = 0; z < zz; z++) + { + itk::Image::IndexType index; + + index[0] = x; + index[1] = y; + index[2] = z; + + mitk::ScalarType pxImage; + mitk::ScalarType pxMask; + + mitkPixelTypeMultiplex5( + mitk::FastSinglePixelAccess, + image->GetChannelDescriptor().GetPixelType(), + image, + image->GetVolumeData(), + index, + pxImage, + 0); + + mitkPixelTypeMultiplex5( + mitk::FastSinglePixelAccess, + mask->GetChannelDescriptor().GetPixelType(), + mask, + mask->GetVolumeData(), + index, + pxMask, + 0); + + //Check if voxel is contained in segmentation + if (pxMask > 0) + { + dataset1ArrU->InsertNextValue(x*xd); + dataset2ArrU->InsertNextValue(y*yd); + dataset3ArrU->InsertNextValue(z*zd); + + if (pxImage == pxImage) + { + dataset1Arr->InsertNextValue(x*xd); + dataset2Arr->InsertNextValue(y*yd); + dataset3Arr->InsertNextValue(z*zd); + } + } + } + } + } + + vtkSmartPointer datasetTable = vtkSmartPointer::New(); + datasetTable->AddColumn(dataset1Arr); + datasetTable->AddColumn(dataset2Arr); + datasetTable->AddColumn(dataset3Arr); + + vtkSmartPointer datasetTableU = vtkSmartPointer::New(); + datasetTableU->AddColumn(dataset1ArrU); + datasetTableU->AddColumn(dataset2ArrU); + datasetTableU->AddColumn(dataset3ArrU); + + vtkSmartPointer pcaStatistics = vtkSmartPointer::New(); + pcaStatistics->SetInputData(vtkStatisticsAlgorithm::INPUT_DATA, datasetTable); + pcaStatistics->SetColumnStatus("M1", 1); + pcaStatistics->SetColumnStatus("M2", 1); + pcaStatistics->SetColumnStatus("M3", 1); + pcaStatistics->RequestSelectedColumns(); + pcaStatistics->SetDeriveOption(true); + pcaStatistics->Update(); + + vtkSmartPointer eigenvalues = vtkSmartPointer::New(); + pcaStatistics->GetEigenvalues(eigenvalues); + + pcaStatistics->SetInputData(vtkStatisticsAlgorithm::INPUT_DATA, datasetTableU); + pcaStatistics->Update(); + vtkSmartPointer eigenvaluesU = vtkSmartPointer::New(); + pcaStatistics->GetEigenvalues(eigenvaluesU); + + std::vector eigen_val(3); + std::vector eigen_valUC(3); + eigen_val[2] = eigenvalues->GetValue(0); + eigen_val[1] = eigenvalues->GetValue(1); + eigen_val[0] = eigenvalues->GetValue(2); + eigen_valUC[2] = eigenvaluesU->GetValue(0); + eigen_valUC[1] = eigenvaluesU->GetValue(1); + eigen_valUC[0] = eigenvaluesU->GetValue(2); + + double major = 4*sqrt(eigen_val[2]); + double minor = 4*sqrt(eigen_val[1]); + double least = 4*sqrt(eigen_val[0]); + double elongation = (major == 0) ? 0 : sqrt(eigen_val[1] / eigen_val[2]); + double flatness = (major == 0) ? 0 : sqrt(eigen_val[0] / eigen_val[2]); + double majorUC = 4*sqrt(eigen_valUC[2]); + double minorUC = 4*sqrt(eigen_valUC[1]); + double leastUC = 4*sqrt(eigen_valUC[0]); + double elongationUC = majorUC == 0 ? 0 : sqrt(eigen_valUC[1] / eigen_valUC[2]); + double flatnessUC = majorUC == 0 ? 0 : sqrt(eigen_valUC[0] / eigen_valUC[2]); + + std::string prefix = FeatureDescriptionPrefix(); + featureList.push_back(std::make_pair(prefix + "Volume (mesh based)",meshVolume)); + featureList.push_back(std::make_pair(prefix + "Surface (mesh based)",meshSurf)); + featureList.push_back(std::make_pair(prefix + "Surface to volume ratio (mesh based)",surfaceToVolume)); + featureList.push_back(std::make_pair(prefix + "Sphericity (mesh based)",sphericity)); + featureList.push_back(std::make_pair(prefix + "Asphericity (mesh based)", asphericity)); + featureList.push_back(std::make_pair(prefix + "Compactness 1 (mesh based)", compactness3)); + featureList.push_back(std::make_pair(prefix + "Compactness 1 old (mesh based)" ,compactness1)); + featureList.push_back(std::make_pair(prefix + "Compactness 2 (mesh based)",compactness2)); + featureList.push_back(std::make_pair(prefix + "Spherical disproportion (mesh based)", sphericalDisproportion)); + featureList.push_back(std::make_pair(prefix + "Surface to volume ratio (voxel based)", surfaceToVolumePixel)); + featureList.push_back(std::make_pair(prefix + "Sphericity (voxel based)", sphericityPixel)); + featureList.push_back(std::make_pair(prefix + "Asphericity (voxel based)", asphericityPixel)); + featureList.push_back(std::make_pair(prefix + "Compactness 1 (voxel based)", compactness3Pixel)); + featureList.push_back(std::make_pair(prefix + "Compactness 1 old (voxel based)", compactness1Pixel)); + featureList.push_back(std::make_pair(prefix + "Compactness 2 (voxel based)", compactness2Pixel)); + featureList.push_back(std::make_pair(prefix + "Spherical disproportion (voxel based)", sphericalDisproportionPixel)); + featureList.push_back(std::make_pair(prefix + "PCA Major axis length",major)); + featureList.push_back(std::make_pair(prefix + "PCA Minor axis length",minor)); + featureList.push_back(std::make_pair(prefix + "PCA Least axis length",least)); + featureList.push_back(std::make_pair(prefix + "PCA Elongation",elongation)); + featureList.push_back(std::make_pair(prefix + "PCA Flatness",flatness)); + featureList.push_back(std::make_pair(prefix + "PCA Major axis length (uncorrected)", majorUC)); + featureList.push_back(std::make_pair(prefix + "PCA Minor axis length (uncorrected)", minorUC)); + featureList.push_back(std::make_pair(prefix + "PCA Least axis length (uncorrected)", leastUC)); + featureList.push_back(std::make_pair(prefix + "PCA Elongation (uncorrected)", elongationUC)); + featureList.push_back(std::make_pair(prefix + "PCA Flatness (uncorrected)", flatnessUC)); return featureList; } mitk::GIFVolumetricStatistics::FeatureNameListType mitk::GIFVolumetricStatistics::GetFeatureNames() { FeatureNameListType featureList; - featureList.push_back("Volumetric Features Compactness 1"); - featureList.push_back("Volumetric Features Compactness 2"); - featureList.push_back("Volumetric Features Sphericity"); - featureList.push_back("Volumetric Features Surface to volume ratio"); - featureList.push_back("Volumetric Features Surface area"); - featureList.push_back("Volumetric Features Volume (mesh based)"); - featureList.push_back("Volumetric Features Volume (pixel based)"); - featureList.push_back("Volumetric Features Spherical disproportion"); - featureList.push_back("Volumetric Features Maximum 3D diameter"); return featureList; } + + +void mitk::GIFVolumetricStatistics::AddArguments(mitkCommandLineParser &parser) +{ + std::string name = GetOptionPrefix(); + + parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Volume-Statistic", "calculates volume based features", us::Any()); +} + +void +mitk::GIFVolumetricStatistics::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &, FeatureListType &featureList) +{ + auto parsedArgs = GetParameter(); + if (parsedArgs.count(GetLongName())) + { + MITK_INFO << "Start calculating Volumetric Features::...."; + auto localResults = this->CalculateFeatures(feature, mask); + featureList.insert(featureList.end(), localResults.begin(), localResults.end()); + MITK_INFO << "Finished calculating volumetric features...."; + } +} + diff --git a/Modules/Classification/CLUtilities/src/MiniAppUtils/mitkGlobalImageFeaturesParameter.cpp b/Modules/Classification/CLUtilities/src/MiniAppUtils/mitkGlobalImageFeaturesParameter.cpp new file mode 100644 index 0000000000..c1a7c96d99 --- /dev/null +++ b/Modules/Classification/CLUtilities/src/MiniAppUtils/mitkGlobalImageFeaturesParameter.cpp @@ -0,0 +1,220 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include + + +#include +#include +#include + + +static bool fileExists(const std::string& filename) +{ + std::ifstream infile(filename.c_str()); + bool isGood = infile.good(); + infile.close(); + return isGood; +} + + +void mitk::cl::GlobalImageFeaturesParameter::AddParameter(mitkCommandLineParser &parser) +{ + // Required Parameter + parser.addArgument("image", "i", mitkCommandLineParser::InputImage, "Input Image", "Path to the input image file", us::Any(), false); + parser.addArgument("mask", "m", mitkCommandLineParser::InputImage, "Input Mask", "Path to the mask Image that specifies the area over for the statistic (Values = 1)", us::Any(), false); + parser.addArgument("morph-mask", "morph", mitkCommandLineParser::InputImage, "Morphological Image Mask", "Path to the mask Image that specifies the area over for the statistic (Values = 1)", us::Any()); + parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output text file", "Path to output file. The output statistic is appended to this file.", us::Any(), false); + + // Optional Parameter + parser.addArgument("logfile", "log", mitkCommandLineParser::InputFile, "Text Logfile", "Path to the location of the target log file. ", us::Any()); + parser.addArgument("save-image", "save-image", mitkCommandLineParser::OutputFile, "Output Image", "If spezified, the image that is used for the analysis is saved to this location.", us::Any()); + parser.addArgument("save-mask", "save-mask", mitkCommandLineParser::OutputFile, "Output Image", "If spezified, the mask that is used for the analysis is saved to this location. ", us::Any()); + parser.addArgument("save-image-screenshots", "save-screenshot", mitkCommandLineParser::OutputFile, "Output Image", "If spezified, a screenshot of each slice is saved. Specify an EXISTING folder with prefix (for example ~/demo/ or ~/demo/image-", us::Any()); + + parser.addArgument("header", "head", mitkCommandLineParser::Bool, "Add Header (Labels) to output", "", us::Any()); + parser.addArgument("first-line-header", "fl-head", mitkCommandLineParser::Bool, "Add Header (Labels) to first line of output", "", us::Any()); + parser.addArgument("decimal-point", "decimal", mitkCommandLineParser::String, "Decima Point that is used in Conversion", "", us::Any()); + + parser.addArgument("resample-mask", "rm", mitkCommandLineParser::Bool, "Bool", "Resamples the mask to the resolution of the input image ", us::Any()); + parser.addArgument("same-space", "sp", mitkCommandLineParser::Bool, "Bool", "Set the spacing of all images to equal. Otherwise an error will be thrown. ", us::Any()); + parser.addArgument("fixed-isotropic", "fi", mitkCommandLineParser::Float, "Float", "Input image resampled to fixed isotropic resolution given in mm. Should be used with resample-mask ", us::Any()); + + parser.addArgument("minimum-intensity", "minimum", mitkCommandLineParser::Float, "Float", "Minimum intensity. If set, it is overwritten by more specific intensity minima", us::Any()); + parser.addArgument("maximum-intensity", "maximum", mitkCommandLineParser::Float, "Float", "Maximum intensity. If set, it is overwritten by more specific intensity maxima", us::Any()); + parser.addArgument("bins", "bins", mitkCommandLineParser::Int, "Int", "Number of bins if bins are used. If set, it is overwritten by more specific bin count", us::Any()); + parser.addArgument("binsize", "binsize", mitkCommandLineParser::Float, "Int", "Size of bins that is used. If set, it is overwritten by more specific bin count", us::Any()); + parser.addArgument("ignore-mask-for-histogram", "ignore-mask", mitkCommandLineParser::Bool, "Bool", "If the whole image is used to calculate the histogram. ", us::Any()); + parser.addArgument("encode-parameter-in-name", "encode-parameter", mitkCommandLineParser::Bool, "Bool", "If true, the parameters used for each feature is encoded in its name. ", us::Any()); +} + +void mitk::cl::GlobalImageFeaturesParameter::ParseParameter(std::map parsedArgs) +{ + ParseFileLocations(parsedArgs); + ParseAdditionalOutputs(parsedArgs); + ParseHeaderInformation(parsedArgs); + ParseMaskAdaptation(parsedArgs); + ParseGlobalFeatureParameter(parsedArgs); +} + +void mitk::cl::GlobalImageFeaturesParameter::ParseFileLocations(std::map &parsedArgs) +{ + + // + // Read input and output file informations + // + imagePath = parsedArgs["image"].ToString(); + maskPath = parsedArgs["mask"].ToString(); + outputPath = parsedArgs["output"].ToString(); + + imageFolder = itksys::SystemTools::GetFilenamePath(imagePath); + imageName = itksys::SystemTools::GetFilenameName(imagePath); + maskFolder = itksys::SystemTools::GetFilenamePath(maskPath); + maskName = itksys::SystemTools::GetFilenameName(maskPath); + + useMorphMask = false; + if (parsedArgs.count("morph-mask")) + { + useMorphMask = true; + morphPath = parsedArgs["morph-mask"].ToString(); + morphName = itksys::SystemTools::GetFilenameName(morphPath); + } + +} + +void mitk::cl::GlobalImageFeaturesParameter::ParseAdditionalOutputs(std::map &parsedArgs) +{ + + // + // Read input and output file informations + // + useLogfile = false; + if (parsedArgs.count("logfile")) + { + useLogfile = true; + logfilePath = us::any_cast(parsedArgs["logfile"]); + } + writeAnalysisImage = false; + if (parsedArgs.count("save-image")) + { + writeAnalysisImage = true; + anaylsisImagePath = us::any_cast(parsedArgs["save-image"]); + } + writeAnalysisMask = false; + if (parsedArgs.count("save-mask")) + { + writeAnalysisMask = true; + analysisMaskPath = us::any_cast(parsedArgs["save-mask"]); + } + writePNGScreenshots = false; + if (parsedArgs.count("save-image-screenshots")) + { + writePNGScreenshots = true; + pngScreenshotsPath = us::any_cast(parsedArgs["save-image-screenshots"]); + std::string pngScrenshotFolderPath = itksys::SystemTools::GetFilenamePath(pngScreenshotsPath); + if (pngScreenshotsPath.back() == '/' || pngScreenshotsPath.back() == '\\') + { + pngScrenshotFolderPath = pngScreenshotsPath; + } + itk::FileTools::CreateDirectory(pngScrenshotFolderPath.c_str()); + } + useDecimalPoint = false; + if (parsedArgs.count("decimal-point")) + { + auto tmpDecimalPoint = us::any_cast(parsedArgs["decimal-point"]); + if (tmpDecimalPoint.length() > 0) + { + useDecimalPoint = true; + decimalPoint = tmpDecimalPoint.at(0); + } + } + +} + +void mitk::cl::GlobalImageFeaturesParameter::ParseHeaderInformation(std::map &parsedArgs) +{ + // + // Check if an header is required or not. Consider also first line header option. + // + useHeader = false; + useHeaderForFirstLineOnly = false; + if (parsedArgs.count("header")) + { + useHeader = us::any_cast(parsedArgs["header"]); + } + if (parsedArgs.count("first-line-header")) + { + useHeaderForFirstLineOnly = us::any_cast(parsedArgs["first-line-header"]); + } + if (useHeaderForFirstLineOnly) + { + useHeader = !fileExists(outputPath); + } +} + +void mitk::cl::GlobalImageFeaturesParameter::ParseMaskAdaptation(std::map &parsedArgs) +{ + // + // Parse parameters that control how the input mask is adapted to the input image + // + resampleMask = false; + ensureSameSpace = false; + resampleToFixIsotropic = false; + resampleResolution = 1.0; + if (parsedArgs.count("resample-mask")) + { + resampleMask = us::any_cast(parsedArgs["resample-mask"]); + } + if (parsedArgs.count("same-space")) + { + ensureSameSpace = us::any_cast(parsedArgs["same-space"]); + } + if (parsedArgs.count("fixed-isotropic")) + { + resampleToFixIsotropic = true; + resampleResolution = us::any_cast(parsedArgs["fixed-isotropic"]); + } +} + +void mitk::cl::GlobalImageFeaturesParameter::ParseGlobalFeatureParameter(std::map &parsedArgs) +{ + // + // Parse parameters that control how the input mask is adapted to the input image + // + defineGlobalMinimumIntensity = false; + defineGlobalMaximumIntensity = false; + defineGlobalNumberOfBins = false; + encodeParameter = false; + if (parsedArgs.count("minimum-intensity")) + { + defineGlobalMinimumIntensity = true; + globalMinimumIntensity = us::any_cast(parsedArgs["minimum-intensity"]); + } + if (parsedArgs.count("maximum-intensity")) + { + defineGlobalMaximumIntensity = true; + globalMaximumIntensity = us::any_cast(parsedArgs["maximum-intensity"]); + } + if (parsedArgs.count("bins")) + { + defineGlobalNumberOfBins = true; + globalNumberOfBins = us::any_cast(parsedArgs["bins"]); + } + if (parsedArgs.count("encode-parameter-in-name")) + { + encodeParameter = true; + } +} diff --git a/Modules/Classification/CLUtilities/src/MiniAppUtils/mitkSplitParameterToVector.cpp b/Modules/Classification/CLUtilities/src/MiniAppUtils/mitkSplitParameterToVector.cpp new file mode 100644 index 0000000000..f1051a9285 --- /dev/null +++ b/Modules/Classification/CLUtilities/src/MiniAppUtils/mitkSplitParameterToVector.cpp @@ -0,0 +1,61 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include + +#include + +std::vector mitk::cl::splitDouble(std::string str, char delimiter) { + std::vector internal; + std::stringstream ss(str); // Turn the string into a stream. + std::string tok; + double val; + while (std::getline(ss, tok, delimiter)) { + std::stringstream s2(tok); + s2 >> val; + internal.push_back(val); + } + + return internal; +} + +std::vector mitk::cl::splitInt(std::string str, char delimiter) { + std::vector internal; + std::stringstream ss(str); // Turn the string into a stream. + std::string tok; + int val; + while (std::getline(ss, tok, delimiter)) { + std::stringstream s2(tok); + s2 >> val; + internal.push_back(val); + } + + return internal; +} + +std::vector mitk::cl::splitString(std::string str, char delimiter) { + std::vector internal; + std::stringstream ss(str); // Turn the string into a stream. + std::string tok; + std::string val; + while (std::getline(ss, tok, delimiter)) { + std::stringstream s2(tok); + s2 >> val; + internal.push_back(val); + } + + return internal; +} \ No newline at end of file diff --git a/Modules/Classification/CLUtilities/src/mitkCLResultWritter.cpp b/Modules/Classification/CLUtilities/src/mitkCLResultWritter.cpp new file mode 100644 index 0000000000..b2b5fa14e6 --- /dev/null +++ b/Modules/Classification/CLUtilities/src/mitkCLResultWritter.cpp @@ -0,0 +1,188 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include + +#include +#include + +template +class punct_facet : public std::numpunct { +public: + punct_facet(charT sep) : + m_Sep(sep) + { + + } +protected: + charT do_decimal_point() const { return m_Sep; } +private: + charT m_Sep; +}; + +mitk::cl::FeatureResultWritter::FeatureResultWritter(std::string file, int mode) : +m_Mode(mode), +m_CurrentRow(0), +m_CurrentElement(0), +m_Separator(";"), +m_SubjectInformation(""), +m_UsedSubjectInformation(false), +m_UseSpecialDecimalPoint(false), +m_DecimalPoint('.') +{ + std::string str; + m_List.push_back(str); + + m_Output.open(file, std::ios::app); +} + +mitk::cl::FeatureResultWritter::~FeatureResultWritter() +{ + for (std::size_t i = 0; i < m_List.size() - 1; ++i) + { + m_Output << m_List[i] << std::endl; + } + //m_Output << "EndOfFile" << std::endl; + m_Output.close(); +} + +void mitk::cl::FeatureResultWritter::SetDecimalPoint(char decimal) +{ + m_Output.imbue(std::locale(std::cout.getloc(), new punct_facet(decimal))); + m_UseSpecialDecimalPoint = true; + m_DecimalPoint = decimal; +} + +void mitk::cl::FeatureResultWritter::AddColumn(double value) { + std::ostringstream ss; + if (m_UseSpecialDecimalPoint) + { + ss.imbue(std::locale(std::cout.getloc(), new punct_facet(m_DecimalPoint))); + } + ss << value; + AddColumn(ss.str()); +} + +void mitk::cl::FeatureResultWritter::AddSubjectInformation(std::string value) { + if ((m_Mode == 0) || (m_Mode == 1)) + { + AddColumn(value); + } + else + { + if (m_UsedSubjectInformation) + { + m_SubjectInformation = ""; + } + m_SubjectInformation += value + m_Separator; + m_UsedSubjectInformation = false; + } +} + +void mitk::cl::FeatureResultWritter::AddColumn(std::string value) { + if ((m_Mode == 0) || (m_Mode == 2)) + { + m_List[m_CurrentRow] = m_List[m_CurrentRow] + value + m_Separator; + } + else + { + m_CurrentRow++; + while (m_List.size() <= m_CurrentRow) + { + std::string str; + m_List.push_back(str); + } + m_List[m_CurrentRow] = m_List[m_CurrentRow] + value + m_Separator; + } +} + + +void mitk::cl::FeatureResultWritter::NewRow(std::string endName) { + AddColumn(endName); + if ((m_Mode == 0) || (m_Mode == 2)) + { + m_CurrentRow++; + while (m_List.size() <= m_CurrentRow) + { + std::string str; + m_List.push_back(str); + } + } + else + { + m_CurrentRow = 0; + } +} + +void mitk::cl::FeatureResultWritter::AddResult(std::string desc, int slice, mitk::AbstractGlobalImageFeature::FeatureListType stats, bool, bool withDescription) +{ + if (m_Mode == 2) + { + for (std::size_t i = 0; i < stats.size(); ++i) + { + if (withDescription) + { + AddColumn(desc); + } + if (slice >= 0) + { + AddColumn(slice); + } + AddColumn(m_SubjectInformation + stats[i].first); + AddColumn(stats[i].second); + NewRow(""); + ++m_CurrentElement; + } + m_UsedSubjectInformation = true; + } + else + { + if (withDescription) + { + AddColumn(desc); + } + if (slice >= 0) + { + AddColumn(slice); + } + for (std::size_t i = 0; i < stats.size(); ++i) + { + AddColumn(stats[i].second); + } + NewRow("EndOfMeasurement"); + ++m_CurrentElement; + } +} + +void mitk::cl::FeatureResultWritter::AddHeader(std::string, int slice, mitk::AbstractGlobalImageFeature::FeatureListType stats, bool withHeader, bool withDescription) +{ + if ((withHeader) && (m_CurrentElement == 0)) + { + if (withDescription) + { + AddColumn("Description"); + } + if (slice >= 0) + { + AddColumn("SliceNumber"); + } + for (std::size_t i = 0; i < stats.size(); ++i) + { + AddColumn(stats[i].first); + } + NewRow("EndOfMeasurement"); + } +} \ No newline at end of file diff --git a/Modules/Classification/CLUtilities/src/mitkCLUtil.cpp b/Modules/Classification/CLUtilities/src/mitkCLUtil.cpp index 6f22880b1b..4ac1ecd762 100644 --- a/Modules/Classification/CLUtilities/src/mitkCLUtil.cpp +++ b/Modules/Classification/CLUtilities/src/mitkCLUtil.cpp @@ -1,482 +1,630 @@ #ifndef _mitkCLUtil_HXX #define _mitkCLUtil_HXX #include #include #include #include // itk includes #include #include +#include "itkHessianRecursiveGaussianImageFilter.h" +#include "itkUnaryFunctorImageFilter.h" +#include "vnl/algo/vnl_symmetric_eigensystem.h" +#include +#include + // Morphologic Operations #include #include #include #include #include #include #include #include // Image Filter #include +#include void mitk::CLUtil::ProbabilityMap(const mitk::Image::Pointer & image , double mean, double stddev, mitk::Image::Pointer & outimage) { AccessFixedDimensionByItk_3(image, mitk::CLUtil::itkProbabilityMap, 3, mean, stddev, outimage); } void mitk::CLUtil::ErodeGrayscale(mitk::Image::Pointer & image , unsigned int radius, mitk::CLUtil::MorphologicalDimensions d, mitk::Image::Pointer & outimage) { AccessFixedDimensionByItk_3(image, mitk::CLUtil::itkErodeGrayscale, 3, outimage, radius, d); } void mitk::CLUtil::DilateGrayscale(mitk::Image::Pointer & image, unsigned int radius, mitk::CLUtil::MorphologicalDimensions d, mitk::Image::Pointer & outimage) { AccessFixedDimensionByItk_3(image, mitk::CLUtil::itkDilateGrayscale, 3, outimage, radius, d); } void mitk::CLUtil::FillHoleGrayscale(mitk::Image::Pointer & image, mitk::Image::Pointer & outimage) { AccessFixedDimensionByItk_1(image, mitk::CLUtil::itkFillHoleGrayscale, 3, outimage); } void mitk::CLUtil::InsertLabel(mitk::Image::Pointer & image, mitk::Image::Pointer & maskImage, unsigned int label) { AccessByItk_2(image, mitk::CLUtil::itkInsertLabel, maskImage, label); } void mitk::CLUtil::GrabLabel(mitk::Image::Pointer & image, mitk::Image::Pointer & outimage, unsigned int label) { AccessFixedDimensionByItk_2(image, mitk::CLUtil::itkGrabLabel, 3, outimage, label); } void mitk::CLUtil::ConnectedComponentsImage(mitk::Image::Pointer & image, mitk::Image::Pointer& mask, mitk::Image::Pointer &outimage, unsigned int& num_components) { AccessFixedDimensionByItk_3(image, mitk::CLUtil::itkConnectedComponentsImage,3, mask, outimage, num_components); } void mitk::CLUtil::MergeLabels(mitk::Image::Pointer & img, const std::map & map) { AccessByItk_1(img, mitk::CLUtil::itkMergeLabels, map); } void mitk::CLUtil::CountVoxel(mitk::Image::Pointer image, std::map & map) { AccessByItk_1(image, mitk::CLUtil::itkCountVoxel, map); } void mitk::CLUtil::CountVoxel(mitk::Image::Pointer image, unsigned int label, unsigned int & count) { AccessByItk_2(image, mitk::CLUtil::itkCountVoxel, label, count); } void mitk::CLUtil::CountVoxel(mitk::Image::Pointer image, unsigned int & count) { AccessByItk_1(image, mitk::CLUtil::itkCountVoxel, count); } void mitk::CLUtil::CreateCheckerboardMask(mitk::Image::Pointer image, mitk::Image::Pointer & outimage) { AccessFixedDimensionByItk_1(image, mitk::CLUtil::itkCreateCheckerboardMask,3, outimage); } void mitk::CLUtil::LogicalAndImages(const mitk::Image::Pointer & image1, const mitk::Image::Pointer & image2, mitk::Image::Pointer & outimage) { AccessFixedDimensionByItk_2(image1,itkLogicalAndImages, 3, image2, outimage); } void mitk::CLUtil::InterpolateCheckerboardPrediction(mitk::Image::Pointer checkerboard_prediction, mitk::Image::Pointer & checkerboard_mask, mitk::Image::Pointer & outimage) { AccessFixedDimensionByItk_2(checkerboard_prediction, mitk::CLUtil::itkInterpolateCheckerboardPrediction,3, checkerboard_mask, outimage); } void mitk::CLUtil::GaussianFilter(mitk::Image::Pointer image, mitk::Image::Pointer & smoothed ,double sigma) { AccessFixedDimensionByItk_2(image, mitk::CLUtil::itkGaussianFilter,3, smoothed, sigma); } +void mitk::CLUtil::DifferenceOfGaussianFilter(mitk::Image::Pointer image, mitk::Image::Pointer & smoothed, double sigma1, double sigma2) +{ + AccessFixedDimensionByItk_3(image, mitk::CLUtil::itkDifferenceOfGaussianFilter, 3, smoothed, sigma1, sigma2); +} + +void mitk::CLUtil::LaplacianOfGaussianFilter(mitk::Image::Pointer image, mitk::Image::Pointer & smoothed, double sigma1) +{ + AccessByItk_2(image, mitk::CLUtil::itkLaplacianOfGaussianFilter, sigma1, smoothed); +} + +void mitk::CLUtil::HessianOfGaussianFilter(mitk::Image::Pointer image, std::vector &out, double sigma) +{ + AccessByItk_2(image, mitk::CLUtil::itkHessianOfGaussianFilter, sigma, out); +} + +void mitk::CLUtil::LocalHistogram(mitk::Image::Pointer image, std::vector &out, int Bins, int NeighbourhoodSize) +{ + AccessByItk_3(image, mitk::CLUtil::itkLocalHistograms, out, NeighbourhoodSize, Bins); +} + + void mitk::CLUtil::DilateBinary(mitk::Image::Pointer & sourceImage, mitk::Image::Pointer& resultImage, int factor , MorphologicalDimensions d) { AccessFixedDimensionByItk_3(sourceImage, mitk::CLUtil::itkDilateBinary, 3, resultImage, factor, d); } void mitk::CLUtil::ErodeBinary(mitk::Image::Pointer & sourceImage, mitk::Image::Pointer& resultImage, int factor, MorphologicalDimensions d) { AccessFixedDimensionByItk_3(sourceImage, mitk::CLUtil::itkErodeBinary, 3, resultImage, factor, d); } void mitk::CLUtil::ClosingBinary(mitk::Image::Pointer & sourceImage, mitk::Image::Pointer& resultImage, int factor, MorphologicalDimensions d) { AccessFixedDimensionByItk_3(sourceImage, mitk::CLUtil::itkClosingBinary, 3, resultImage, factor, d); } template void mitk::CLUtil::itkProbabilityMap(const TImageType * sourceImage, double mean, double std_dev, mitk::Image::Pointer& resultImage) { itk::Image::Pointer itk_img = itk::Image::New(); itk_img->SetRegions(sourceImage->GetLargestPossibleRegion()); itk_img->SetOrigin(sourceImage->GetOrigin()); itk_img->SetSpacing(sourceImage->GetSpacing()); itk_img->SetDirection(sourceImage->GetDirection()); itk_img->Allocate(); itk::ImageRegionConstIterator it(sourceImage,sourceImage->GetLargestPossibleRegion()); itk::ImageRegionIterator > outit(itk_img,itk_img->GetLargestPossibleRegion()); while(!it.IsAtEnd()) { double x = it.Value(); double prob = (1.0/(std_dev*std::sqrt(2.0*M_PI))) * std::exp(-(((x-mean)*(x-mean))/(2.0*std_dev*std_dev))); outit.Set(prob); ++it; ++outit; } mitk::CastToMitkImage(itk_img, resultImage); } template< typename TImageType > void mitk::CLUtil::itkInterpolateCheckerboardPrediction(TImageType * checkerboard_prediction, Image::Pointer &checkerboard_mask, mitk::Image::Pointer & outimage) { typename TImageType::Pointer itk_checkerboard_mask; mitk::CastToItkImage(checkerboard_mask,itk_checkerboard_mask); typename TImageType::Pointer itk_outimage = TImageType::New(); itk_outimage->SetRegions(checkerboard_prediction->GetLargestPossibleRegion()); itk_outimage->SetDirection(checkerboard_prediction->GetDirection()); itk_outimage->SetOrigin(checkerboard_prediction->GetOrigin()); itk_outimage->SetSpacing(checkerboard_prediction->GetSpacing()); itk_outimage->Allocate(); itk_outimage->FillBuffer(0); //typedef typename itk::ShapedNeighborhoodIterator::SizeType SizeType; typedef itk::Size<3> SizeType; SizeType size; size.Fill(1); itk::ShapedNeighborhoodIterator iit(size,checkerboard_prediction,checkerboard_prediction->GetLargestPossibleRegion()); itk::ShapedNeighborhoodIterator mit(size,itk_checkerboard_mask,itk_checkerboard_mask->GetLargestPossibleRegion()); itk::ImageRegionIterator oit(itk_outimage,itk_outimage->GetLargestPossibleRegion()); typedef typename itk::ShapedNeighborhoodIterator::OffsetType OffsetType; OffsetType offset; offset.Fill(0); offset[0] = 1; // {1,0,0} iit.ActivateOffset(offset); mit.ActivateOffset(offset); offset[0] = -1; // {-1,0,0} iit.ActivateOffset(offset); mit.ActivateOffset(offset); offset[0] = 0; offset[1] = 1; //{0,1,0} iit.ActivateOffset(offset); mit.ActivateOffset(offset); offset[1] = -1; //{0,-1,0} iit.ActivateOffset(offset); mit.ActivateOffset(offset); // iit.ActivateOffset({{0,0,1}}); // iit.ActivateOffset({{0,0,-1}}); // mit.ActivateOffset({{0,0,1}}); // mit.ActivateOffset({{0,0,-1}}); while(!iit.IsAtEnd()) { if(mit.GetCenterPixel() == 0) { typename TImageType::PixelType mean = 0; for (auto i = iit.Begin(); ! i.IsAtEnd(); i++) { mean += i.Get(); } //std::sort(list.begin(),list.end(),[](const typename TImageType::PixelType x,const typename TImageType::PixelType y){return x<=y;}); oit.Set((mean+0.5)/6.0); } else { oit.Set(iit.GetCenterPixel()); } ++iit; ++mit; ++oit; } mitk::CastToMitkImage(itk_outimage,outimage); } template< typename TImageType > void mitk::CLUtil::itkCreateCheckerboardMask(TImageType * image, mitk::Image::Pointer & outimage) { typename TImageType::Pointer zeroimg = TImageType::New(); zeroimg->SetRegions(image->GetLargestPossibleRegion()); zeroimg->SetDirection(image->GetDirection()); zeroimg->SetOrigin(image->GetOrigin()); zeroimg->SetSpacing(image->GetSpacing()); zeroimg->Allocate(); zeroimg->FillBuffer(0); typedef itk::CheckerBoardImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput1(image); filter->SetInput2(zeroimg); typename FilterType::PatternArrayType pattern; pattern.SetElement(0,(image->GetLargestPossibleRegion().GetSize()[0])); pattern.SetElement(1,(image->GetLargestPossibleRegion().GetSize()[1])); pattern.SetElement(2,(image->GetLargestPossibleRegion().GetSize()[2])); filter->SetCheckerPattern(pattern); filter->Update(); mitk::CastToMitkImage(filter->GetOutput(), outimage); } template void mitk::CLUtil::itkSumVoxelForLabel(TImageType* image, const mitk::Image::Pointer & source , typename TImageType::PixelType label, double & val ) { itk::Image::Pointer itk_source; mitk::CastToItkImage(source,itk_source); itk::ImageRegionConstIterator inputIter(image, image->GetLargestPossibleRegion()); itk::ImageRegionConstIterator< itk::Image > sourceIter(itk_source, itk_source->GetLargestPossibleRegion()); while(!inputIter.IsAtEnd()) { if(inputIter.Value() == label) val += sourceIter.Value(); ++inputIter; ++sourceIter; } } template void mitk::CLUtil::itkSqSumVoxelForLabel(TImageType* image, const mitk::Image::Pointer & source, typename TImageType::PixelType label, double & val ) { itk::Image::Pointer itk_source; mitk::CastToItkImage(source,itk_source); itk::ImageRegionConstIterator inputIter(image, image->GetLargestPossibleRegion()); itk::ImageRegionConstIterator< itk::Image > sourceIter(itk_source, itk_source->GetLargestPossibleRegion()); while(!inputIter.IsAtEnd()) { if(inputIter.Value() == label) val += sourceIter.Value() * sourceIter.Value(); ++inputIter; ++sourceIter; } } template void mitk::CLUtil::itkFitStructuringElement(TStructuringElement & se, MorphologicalDimensions d, int factor) { typename TStructuringElement::SizeType size; size.Fill(factor); switch(d) { case(All): case(Axial): size.SetElement(2,0); break; case(Sagital): size.SetElement(0,0); break; case(Coronal): size.SetElement(1,0); break; } se.SetRadius(size); se.CreateStructuringElement(); } template void mitk::CLUtil::itkClosingBinary(TImageType * sourceImage, mitk::Image::Pointer& resultImage, int factor, MorphologicalDimensions d) { typedef itk::BinaryBallStructuringElement BallType; typedef itk::BinaryMorphologicalClosingImageFilter FilterType; BallType strElem; itkFitStructuringElement(strElem,d,factor); typename FilterType::Pointer erodeFilter = FilterType::New(); erodeFilter->SetKernel(strElem); erodeFilter->SetInput(sourceImage); erodeFilter->SetForegroundValue(1); erodeFilter->Update(); mitk::CastToMitkImage(erodeFilter->GetOutput(), resultImage); } template void mitk::CLUtil::itkDilateBinary(TImageType * sourceImage, mitk::Image::Pointer& resultImage, int factor, MorphologicalDimensions d) { typedef itk::BinaryBallStructuringElement BallType; typedef typename itk::BinaryDilateImageFilter BallDilateFilterType; BallType strElem; itkFitStructuringElement(strElem,d,factor); typename BallDilateFilterType::Pointer erodeFilter = BallDilateFilterType::New(); erodeFilter->SetKernel(strElem); erodeFilter->SetInput(sourceImage); erodeFilter->SetDilateValue(1); erodeFilter->Update(); mitk::CastToMitkImage(erodeFilter->GetOutput(), resultImage); } template void mitk::CLUtil::itkErodeBinary(TImageType * sourceImage, mitk::Image::Pointer& resultImage, int factor, MorphologicalDimensions d) { typedef itk::BinaryBallStructuringElement BallType; typedef typename itk::BinaryErodeImageFilter BallErodeFilterType; BallType strElem; itkFitStructuringElement(strElem,d,factor); typename BallErodeFilterType::Pointer erodeFilter = BallErodeFilterType::New(); erodeFilter->SetKernel(strElem); erodeFilter->SetInput(sourceImage); erodeFilter->SetErodeValue(1); // erodeFilter->UpdateLargestPossibleRegion(); erodeFilter->Update(); mitk::CastToMitkImage(erodeFilter->GetOutput(), resultImage); } /// /// \brief itkFillHolesBinary /// \param sourceImage /// \param resultImage /// template void mitk::CLUtil::itkFillHolesBinary(itk::Image* sourceImage, mitk::Image::Pointer& resultImage) { typedef itk::Image ImageType; typedef typename itk::BinaryFillholeImageFilter FillHoleFilterType; typename FillHoleFilterType::Pointer fillHoleFilter = FillHoleFilterType::New(); fillHoleFilter->SetInput(sourceImage); fillHoleFilter->SetForegroundValue(1); fillHoleFilter->Update(); mitk::CastToMitkImage(fillHoleFilter->GetOutput(), resultImage); } /// /// \brief itkLogicalAndImages /// \param image1 keep the values of image 1 /// \param image2 /// template void mitk::CLUtil::itkLogicalAndImages(const TImageType * image1, const mitk::Image::Pointer & image2, mitk::Image::Pointer & outimage) { typename TImageType::Pointer itk_outimage = TImageType::New(); itk_outimage->SetRegions(image1->GetLargestPossibleRegion()); itk_outimage->SetDirection(image1->GetDirection()); itk_outimage->SetOrigin(image1->GetOrigin()); itk_outimage->SetSpacing(image1->GetSpacing()); itk_outimage->Allocate(); itk_outimage->FillBuffer(0); typename TImageType::Pointer itk_image2; mitk::CastToItkImage(image2,itk_image2); itk::ImageRegionConstIterator it1(image1, image1->GetLargestPossibleRegion()); itk::ImageRegionConstIterator it2(itk_image2, itk_image2->GetLargestPossibleRegion()); itk::ImageRegionIterator oit(itk_outimage,itk_outimage->GetLargestPossibleRegion()); while(!it1.IsAtEnd()) { if(it1.Value() == 0 || it2.Value() == 0) { oit.Set(0); }else oit.Set(it1.Value()); ++it1; ++it2; ++oit; } mitk::CastToMitkImage(itk_outimage, outimage); } /// /// \brief GaussianFilter /// \param image /// \param smoothed /// \param sigma /// template void mitk::CLUtil::itkGaussianFilter(TImageType * image, mitk::Image::Pointer & smoothed ,double sigma) { typedef itk::DiscreteGaussianImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput(image); filter->SetVariance(sigma); filter->Update(); mitk::CastToMitkImage(filter->GetOutput(),smoothed); } +template +void mitk::CLUtil::itkDifferenceOfGaussianFilter(TImageType * image, mitk::Image::Pointer & smoothed, double sigma1, double sigma2) +{ + typedef itk::DiscreteGaussianImageFilter FilterType; + typedef itk::SubtractImageFilter SubtractFilterType; + typename FilterType::Pointer filter1 = FilterType::New(); + typename FilterType::Pointer filter2 = FilterType::New(); + typename SubtractFilterType::Pointer subFilter = SubtractFilterType::New(); + filter1->SetInput(image); + filter1->SetVariance(sigma1); + filter1->Update(); + filter2->SetInput(image); + filter2->SetVariance(sigma2); + filter2->Update(); + subFilter->SetInput1(filter1->GetOutput()); + subFilter->SetInput2(filter2->GetOutput()); + subFilter->Update(); + + mitk::CastToMitkImage(subFilter->GetOutput(), smoothed); +} + + +template +void mitk::CLUtil::itkLaplacianOfGaussianFilter(itk::Image* itkImage, double variance, mitk::Image::Pointer &output) +{ + typedef itk::Image ImageType; + typedef itk::DiscreteGaussianImageFilter< ImageType, ImageType > GaussFilterType; + typedef itk::LaplacianRecursiveGaussianImageFilter LaplacianFilter; + + typename GaussFilterType::Pointer gaussianFilter = GaussFilterType::New(); + gaussianFilter->SetInput(itkImage); + gaussianFilter->SetVariance(variance); + gaussianFilter->Update(); + typename LaplacianFilter::Pointer laplaceFilter = LaplacianFilter::New(); + laplaceFilter->SetInput(gaussianFilter->GetOutput()); + laplaceFilter->Update(); + mitk::CastToMitkImage(laplaceFilter->GetOutput(), output); +} + +namespace Functor +{ + template + class MatrixFirstEigenvalue + { + public: + MatrixFirstEigenvalue() {} + virtual ~MatrixFirstEigenvalue() {} + + int order; + + inline TOutput operator ()(const TInput& input) + { + double a, b, c; + if (input[0] < 0.01 && input[1] < 0.01 &&input[2] < 0.01 &&input[3] < 0.01 &&input[4] < 0.01 &&input[5] < 0.01) + return 0; + vnl_symmetric_eigensystem_compute_eigenvals(input[0], input[1], input[2], input[3], input[4], input[5], a, b, c); + switch (order) + { + case 0: return a; + case 1: return b; + case 2: return c; + default: return a; + } + } + bool operator !=(const MatrixFirstEigenvalue) const + { + return false; + } + bool operator ==(const MatrixFirstEigenvalue& other) const + { + return !(*this != other); + } + }; +} + +template +void mitk::CLUtil::itkHessianOfGaussianFilter(itk::Image* itkImage, double variance, std::vector &out) +{ + typedef itk::Image ImageType; + typedef itk::Image FloatImageType; + typedef itk::HessianRecursiveGaussianImageFilter HessianFilterType; + typedef typename HessianFilterType::OutputImageType VectorImageType; + typedef Functor::MatrixFirstEigenvalue DeterminantFunctorType; + typedef itk::UnaryFunctorImageFilter DetFilterType; + + typename HessianFilterType::Pointer hessianFilter = HessianFilterType::New(); + hessianFilter->SetInput(itkImage); + hessianFilter->SetSigma(std::sqrt(variance)); + for (unsigned int i = 0; i < VImageDimension; ++i) + { + mitk::Image::Pointer tmpImage = mitk::Image::New(); + typename DetFilterType::Pointer detFilter = DetFilterType::New(); + detFilter->SetInput(hessianFilter->GetOutput()); + detFilter->GetFunctor().order = i; + detFilter->Update(); + mitk::CastToMitkImage(detFilter->GetOutput(), tmpImage); + out.push_back(tmpImage); + } +} + +template +void mitk::CLUtil::itkLocalHistograms(itk::Image* itkImage, std::vector &out, int size, int bins) +{ + typedef itk::Image ImageType; + typedef itk::MultiHistogramFilter MultiHistogramType; + + typename MultiHistogramType::Pointer filter = MultiHistogramType::New(); + filter->SetInput(itkImage); + filter->SetUseImageIntensityRange(true); + filter->SetSize(size); + filter->SetBins(bins); + filter->Update(); + for (int i = 0; i < bins; ++i) + { + mitk::Image::Pointer img = mitk::Image::New(); + mitk::CastToMitkImage(filter->GetOutput(i), img); + out.push_back(img); + } +} + template void mitk::CLUtil::itkErodeGrayscale(TImageType * image, mitk::Image::Pointer & outimage , unsigned int radius, mitk::CLUtil::MorphologicalDimensions d) { typedef itk::BinaryBallStructuringElement StructureElementType; typedef itk::GrayscaleErodeImageFilter FilterType; StructureElementType ball; itkFitStructuringElement(ball,d, radius); typename FilterType::Pointer filter = FilterType::New(); filter->SetKernel(ball); filter->SetInput(image); filter->Update(); mitk::CastToMitkImage(filter->GetOutput(),outimage); } template void mitk::CLUtil::itkDilateGrayscale(TImageType * image, mitk::Image::Pointer & outimage , unsigned int radius, mitk::CLUtil::MorphologicalDimensions d) { typedef itk::BinaryBallStructuringElement StructureElementType; typedef itk::GrayscaleDilateImageFilter FilterType; StructureElementType ball; itkFitStructuringElement(ball,d, radius); typename FilterType::Pointer filter = FilterType::New(); filter->SetKernel(ball); filter->SetInput(image); filter->Update(); mitk::CastToMitkImage(filter->GetOutput(),outimage); } template void mitk::CLUtil::itkFillHoleGrayscale(TImageType * image, mitk::Image::Pointer & outimage) { typedef itk::GrayscaleFillholeImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput(image); filter->Update(); mitk::CastToMitkImage(filter->GetOutput(),outimage); } #endif diff --git a/Modules/Classification/CLUtilities/test/mitkGlobalFeaturesTest.cpp b/Modules/Classification/CLUtilities/test/mitkGlobalFeaturesTest.cpp index 3aea5886b3..fd1c5925bb 100644 --- a/Modules/Classification/CLUtilities/test/mitkGlobalFeaturesTest.cpp +++ b/Modules/Classification/CLUtilities/test/mitkGlobalFeaturesTest.cpp @@ -1,317 +1,316 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include "mitkIOUtil.h" #include #include #include -#include +#include #include #include template static mitk::Image::Pointer GenerateMaskImage(unsigned int dimX, unsigned int dimY, unsigned int dimZ, float spacingX = 1, float spacingY = 1, float spacingZ = 1) { typedef itk::Image< TPixelType, 3 > ImageType; typename ImageType::RegionType imageRegion; imageRegion.SetSize(0, dimX); imageRegion.SetSize(1, dimY); imageRegion.SetSize(2, dimZ); typename ImageType::SpacingType spacing; spacing[0] = spacingX; spacing[1] = spacingY; spacing[2] = spacingZ; mitk::Point3D origin; origin.Fill(0.0); itk::Matrix directionMatrix; directionMatrix.SetIdentity(); typename ImageType::Pointer image = ImageType::New(); image->SetSpacing( spacing ); image->SetOrigin( origin ); image->SetDirection( directionMatrix ); image->SetLargestPossibleRegion( imageRegion ); image->SetBufferedRegion( imageRegion ); image->SetRequestedRegion( imageRegion ); image->Allocate(); image->FillBuffer(1); mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk( image.GetPointer() ); mitkImage->SetVolume( image->GetBufferPointer() ); return mitkImage; } template static mitk::Image::Pointer GenerateGradientWithDimXImage(unsigned int dimX, unsigned int dimY, unsigned int dimZ, float spacingX = 1, float spacingY = 1, float spacingZ = 1) { typedef itk::Image< TPixelType, 3 > ImageType; typename ImageType::RegionType imageRegion; imageRegion.SetSize(0, dimX); imageRegion.SetSize(1, dimY); imageRegion.SetSize(2, dimZ); typename ImageType::SpacingType spacing; spacing[0] = spacingX; spacing[1] = spacingY; spacing[2] = spacingZ; mitk::Point3D origin; origin.Fill(0.0); itk::Matrix directionMatrix; directionMatrix.SetIdentity(); typename ImageType::Pointer image = ImageType::New(); image->SetSpacing( spacing ); image->SetOrigin( origin ); image->SetDirection( directionMatrix ); image->SetLargestPossibleRegion( imageRegion ); image->SetBufferedRegion( imageRegion ); image->SetRequestedRegion( imageRegion ); image->Allocate(); image->FillBuffer(0.0); typedef itk::ImageRegionIterator IteratorOutputType; IteratorOutputType it(image, imageRegion); it.GoToBegin(); TPixelType val = 0; while(!it.IsAtEnd()) { it.Set(val % dimX); val++; ++it; } mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk( image.GetPointer() ); mitkImage->SetVolume( image->GetBufferPointer() ); return mitkImage; } + class mitkGlobalFeaturesTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkGlobalFeaturesTestSuite ); MITK_TEST(FirstOrder_SinglePoint); MITK_TEST(FirstOrder_QubicArea); //MITK_TEST(RunLenght_QubicArea); MITK_TEST(Coocurrence_QubicArea); //MITK_TEST(TestFirstOrderStatistic); // MITK_TEST(TestThreadedDecisionForest); CPPUNIT_TEST_SUITE_END(); private: typedef itk::Image ImageType; typedef itk::Image MaskType; mitk::Image::Pointer m_Image,m_Mask,m_Mask1; ImageType::Pointer m_ItkImage; MaskType::Pointer m_ItkMask,m_ItkMask1; mitk::Image::Pointer m_GradientImage, m_GradientMask; public: void setUp(void) override { // Load Image Data m_Image = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("Pic3D.nrrd"))[0].GetPointer()); mitk::CastToItkImage(m_Image,m_ItkImage); // Create a single mask with only one pixel within the regions mitk::Image::Pointer mask1 = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("Pic3D.nrrd"))[0].GetPointer()); mitk::CastToItkImage(mask1,m_ItkMask); m_ItkMask->FillBuffer(0); MaskType::IndexType index; index[0]=88;index[1]=81;index[2]=13; m_ItkMask->SetPixel(index, 1); MITK_INFO << "Pixel Value: "<GetPixel(index); mitk::CastToMitkImage(m_ItkMask, m_Mask); // Create a mask with a covered region mitk::Image::Pointer lmask1 = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("Pic3D.nrrd"))[0].GetPointer()); mitk::CastToItkImage(lmask1,m_ItkMask1); m_ItkMask1->FillBuffer(0); int range=2; for (int x = 88-range;x < 88+range+1;++x) { for (int y=81-range;y<81+range+1;++y) { for (int z=13-range;z<13+range+1;++z) { index[0] = x; index[1] = y; index[2] = z; //MITK_INFO << "Pixel: " <GetPixel(index); m_ItkMask1->SetPixel(index, 1); } } } mitk::CastToMitkImage(m_ItkMask1, m_Mask1); m_GradientImage=GenerateGradientWithDimXImage(5,5,5); m_GradientMask = GenerateMaskImage(5,5,5); } void FirstOrder_SinglePoint() { mitk::GIFFirstOrderStatistics::Pointer calculator = mitk::GIFFirstOrderStatistics::New(); - calculator->SetHistogramSize(4096); - calculator->SetUseCtRange(true); + //calculator->SetHistogramSize(4096); + //calculator->SetUseCtRange(true); auto features = calculator->CalculateFeatures(m_Image, m_Mask); std::map results; for (auto iter=features.begin(); iter!=features.end();++iter) { results[(*iter).first]=(*iter).second; MITK_INFO << (*iter).first << " : " << (*iter).second; } CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The range of a single pixel should be 0",0.0, results["FirstOrder Range"], 0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The uniformity of a single pixel should be 1",1.0, results["FirstOrder Uniformity"], 0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The entropy of a single pixel should be 0",0.0, results["FirstOrder Entropy"], 0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Root-Means-Square of a single pixel with (-352) should be 352",352.0, results["FirstOrder RMS"], 0.01); CPPUNIT_ASSERT_EQUAL_MESSAGE("The Kurtosis of a single pixel should be undefined",results["FirstOrder Kurtosis"]==results["FirstOrder Kurtosis"], false); CPPUNIT_ASSERT_EQUAL_MESSAGE("The Skewness of a single pixel should be undefined",results["FirstOrder Skewness"]==results["FirstOrder Skewness"], false); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Mean absolute deviation of a single pixel with (-352) should be 0",0, results["FirstOrder Mean absolute deviation"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Covered image intensity range of a single pixel with (-352) should be 0",0, results["FirstOrder Covered Image Intensity Range"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Minimum of a single pixel with (-352) should be -352",-352, results["FirstOrder Minimum"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Maximum of a single pixel with (-352) should be -352",-352, results["FirstOrder Maximum"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Mean of a single pixel with (-352) should be -352",-352, results["FirstOrder Mean"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Variance (corrected) of a single pixel with (-352) should be 0",0, results["FirstOrder Variance"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Sum of a single pixel with (-352) should be -352",-352, results["FirstOrder Sum"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Median of a single pixel with (-352) should be -352",-352, results["FirstOrder Median"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Standard deviation (corrected) of a single pixel with (-352) should be -352",0, results["FirstOrder Standard deviation"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The number of voxels of a single pixel should be 1",1, results["FirstOrder No. of Voxel"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Energy of a single pixel should be 352*352",352*352, results["FirstOrder Energy"], 0.0); // MITK_ASSERT_EQUAL(results["FirstOrder Range"]==0.0,true,"The range of a single pixel should be 0"); } void FirstOrder_QubicArea() { mitk::GIFFirstOrderStatistics::Pointer calculator = mitk::GIFFirstOrderStatistics::New(); - calculator->SetHistogramSize(4096); - calculator->SetUseCtRange(true); + //calculator->SetHistogramSize(4096); + //calculator->SetUseCtRange(true); auto features = calculator->CalculateFeatures(m_Image, m_Mask1); std::map results; for (auto iter=features.begin(); iter!=features.end();++iter) { results[(*iter).first]=(*iter).second; MITK_INFO << (*iter).first << " : " << (*iter).second; } CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The range should be 981",981, results["FirstOrder Range"], 0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Root-Means-Square of a single pixel with (-352) should be 352",402.895778, results["FirstOrder RMS"], 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Minimum of a single pixel with (-352) should be -352",-937, results["FirstOrder Minimum"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Maximum of a single pixel with (-352) should be -352",44, results["FirstOrder Maximum"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Mean of a single pixel with (-352) should be -352",-304.448, results["FirstOrder Mean"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Sum of a single pixel with (-352) should be -352",-38056, results["FirstOrder Sum"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Median of a single pixel with (-352) should be -352",-202, results["FirstOrder Median"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The number of voxels of a single pixel should be 1",125, results["FirstOrder No. of Voxel"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Standard deviation (corrected) of a single pixel with (-352) should be -352",264.949066, results["FirstOrder Standard deviation"], 0.000001); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Energy of a single pixel should be 352*352",20290626, results["FirstOrder Energy"], 0.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The uniformity of a single pixel should be 1",0.0088960, results["FirstOrder Uniformity"], 0.0000001); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The entropy of a single pixel should be 0",-6.853784285, results["FirstOrder Entropy"], 0.000000005); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Variance (corrected) of a single pixel with (-352) should be 0",70198.0074, results["FirstOrder Variance"], 0.0001); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Kurtosis of a single pixel should be 0",2.63480121, results["FirstOrder Kurtosis"], 0.0001); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Skewness of a single pixel should be 0",-0.91817318, results["FirstOrder Skewness"], 0.00001); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Mean absolute deviation of a single pixel with (-352) should be 0",219.348608, results["FirstOrder Mean absolute deviation"], 0.000001); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The Covered image intensity range of a single pixel with (-352) should be 0",0.41149329, results["FirstOrder Covered Image Intensity Range"], 0.000001); } void RunLenght_QubicArea() { - mitk::GIFGrayLevelRunLength::Pointer calculator = mitk::GIFGrayLevelRunLength::New(); + mitk::GIFGreyLevelRunLength::Pointer calculator = mitk::GIFGreyLevelRunLength::New(); //calculator->SetHistogramSize(4096); - calculator->SetUseCtRange(true); - calculator->SetRange(981); auto features = calculator->CalculateFeatures(m_Image, m_Mask1); std::map results; for (auto iter=features.begin(); iter!=features.end();++iter) { results[(*iter).first]=(*iter).second; MITK_INFO << (*iter).first << " : " << (*iter).second; } } void Coocurrence_QubicArea() { /* * Expected Matrix: (Direction 0,0,1) * |------------------------| * | 20 | 0 | 0 | 0 | 0 | * |------------------------| * | 0 | 20 | 0 | 0 | 0 | * |------------------------| * | 0 | 0 | 20 | 0 | 0 | * |------------------------| * | 0 | 0 | 0 | 20 | 0 | * |------------------------| * | 0 | 0 | 0 | 0 | 20 | * |------------------------| * Expected Matrix: (Direction (1,0,0),(0,1,0)) * |------------------------| * | 20 | 0 | 0 | 0 | 0 | * |------------------------| * | 20 | 0 | 0 | 0 | 0 | * |------------------------| * | 20 | 0 | 0 | 0 | 0 | * |------------------------| * | 20 | 0 | 0 | 0 | 0 | * |------------------------| * | 20 | 0 | 0 | 0 | 0 | * |------------------------| */ mitk::GIFCooccurenceMatrix::Pointer calculator = mitk::GIFCooccurenceMatrix::New(); //calculator->SetHistogramSize(4096); //calculator->SetUseCtRange(true); //calculator->SetRange(981); calculator->SetDirection(1); auto features = calculator->CalculateFeatures(m_GradientImage, m_GradientMask); std::map results; for (auto iter=features.begin(); iter!=features.end();++iter) { results[(*iter).first]=(*iter).second; MITK_INFO << (*iter).first << " : " << (*iter).second; } CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The mean energy value should be 0.2",0.2, results["co-occ. (1) Energy Means"], mitk::eps); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The mean entropy value should be 0.2",2.321928, results["co-occ. (1) Entropy Means"], 0.000001); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The mean contrast value should be 0.0",0, results["co-occ. (1) Contrast Means"], mitk::eps); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The mean dissimilarity value should be 0.0",0, results["co-occ. (1) Dissimilarity Means"], mitk::eps); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The mean homogenity1 value should be 1.0",1, results["co-occ. (1) Homogeneity1 Means"], mitk::eps); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("The mean InverseDifferenceMoment value should be 1.0",1, results["co-occ. (1) InverseDifferenceMoment Means"], mitk::eps); } }; MITK_TEST_SUITE_REGISTRATION(mitkGlobalFeatures) \ No newline at end of file diff --git a/Modules/Classification/CLVigraRandomForest/files.cmake b/Modules/Classification/CLVigraRandomForest/files.cmake index eddacbdbf5..358f417862 100644 --- a/Modules/Classification/CLVigraRandomForest/files.cmake +++ b/Modules/Classification/CLVigraRandomForest/files.cmake @@ -1,22 +1,25 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkModuleActivator.cpp Classifier/mitkVigraRandomForestClassifier.cpp + Classifier/mitkPURFClassifier.cpp Algorithm/itkHessianMatrixEigenvalueImageFilter.cpp Algorithm/itkStructureTensorEigenvalueImageFilter.cpp + Splitter/mitkAdditionalRFData.cpp Splitter/mitkImpurityLoss.cpp + Splitter/mitkPUImpurityLoss.cpp Splitter/mitkLinearSplitting.cpp Splitter/mitkThresholdSplit.cpp IO/mitkRandomForestIO.cpp IO/mitkVigraRandomForestClassifierSerializer.cpp IO/mitkDummyLsetReader.cpp ) set( TOOL_FILES ) diff --git a/Modules/Classification/CLVigraRandomForest/include/mitkAdditionalRFData.h b/Modules/Classification/CLVigraRandomForest/include/mitkAdditionalRFData.h new file mode 100644 index 0000000000..608579700b --- /dev/null +++ b/Modules/Classification/CLVigraRandomForest/include/mitkAdditionalRFData.h @@ -0,0 +1,33 @@ +#ifndef mitkAdditionalRFData_h +#define mitkAdditionalRFData_h + +#include + + +namespace mitk +{ + class AdditionalRFDataAbstract + { + public: + // This function is necessary to be able to do dynamic casts + virtual void NoFunction() = 0; + virtual ~AdditionalRFDataAbstract() {}; + }; + + class NoRFData : public AdditionalRFDataAbstract + { + public: + virtual void NoFunction() { return; } + virtual ~NoRFData() {}; + }; + + class PURFData : public AdditionalRFDataAbstract + { + public: + vigra::ArrayVector m_Kappa; + virtual void NoFunction(); + virtual ~PURFData() {}; + }; +} + +#endif //mitkAdditionalRFData_h diff --git a/Modules/Classification/CLVigraRandomForest/include/mitkImpurityLoss.h b/Modules/Classification/CLVigraRandomForest/include/mitkImpurityLoss.h index abce530788..f36e5438aa 100644 --- a/Modules/Classification/CLVigraRandomForest/include/mitkImpurityLoss.h +++ b/Modules/Classification/CLVigraRandomForest/include/mitkImpurityLoss.h @@ -1,55 +1,57 @@ #ifndef mitkImpurityLoss_h #define mitkImpurityLoss_h #include #include +#include namespace mitk { template , class TWeightContainer = vigra::MultiArrayView<2, double> > class ImpurityLoss { public: typedef TLabelContainer LabelContainerType; typedef TWeightContainer WeightContainerType; template ImpurityLoss(TLabelContainer const &labels, - vigra::ProblemSpec const &ext); + vigra::ProblemSpec const &ext, + AdditionalRFDataAbstract *data); void Reset(); template double Increment(TDataIterator begin, TDataIterator end); template double Decrement(TDataIterator begin, TDataIterator end); template double Init(TArray initCounts); vigra::ArrayVector const& Response(); void UsePointWeights(bool useWeights); bool IsUsingPointWeights(); void SetPointWeights(TWeightContainer weight); WeightContainerType GetPointWeights(); private: bool m_UsePointWeights; TWeightContainer m_PointWeights; //Variable of origin TLabelContainer const& m_Labels; vigra::ArrayVector m_Counts; vigra::ArrayVector m_ClassWeights; double m_TotalCount; TLossFunction m_LossFunction; }; } #include <../src/Splitter/mitkImpurityLoss.cpp> #endif //mitkImpurityLoss_h diff --git a/Modules/Classification/CLVigraRandomForest/include/mitkLinearSplitting.h b/Modules/Classification/CLVigraRandomForest/include/mitkLinearSplitting.h index 31e4ab7a73..46dbb31299 100644 --- a/Modules/Classification/CLVigraRandomForest/include/mitkLinearSplitting.h +++ b/Modules/Classification/CLVigraRandomForest/include/mitkLinearSplitting.h @@ -1,86 +1,91 @@ #ifndef mitkLinearSplitting_h #define mitkLinearSplitting_h #include #include +#include namespace mitk { template class LinearSplitting { public: typedef typename TLossAccumulator::WeightContainerType TWeightContainer; typedef TWeightContainer WeightContainerType; LinearSplitting(); template LinearSplitting(vigra::ProblemSpec const &ext); void UsePointWeights(bool pointWeight); bool IsUsingPointWeights(); void UseRandomSplit(bool randomSplit); bool IsUsingRandomSplit(); void SetPointWeights(WeightContainerType weight); WeightContainerType GetPointWeights(); + void SetAdditionalData(AdditionalRFDataAbstract* data); + AdditionalRFDataAbstract* GetAdditionalData() const; + template void set_external_parameters(vigra::ProblemSpec const &ext); template void operator()(TDataSourceFeature const &column, TDataSourceLabel const &labels, TDataIterator &begin, TDataIterator &end, TArray const ®ionResponse); template double LossOfRegion(TDataSourceLabel const & labels, TDataIterator &begin, TDataIterator &end, TArray const & regionResponse); double GetMinimumLoss() { return m_MinimumLoss; } double GetMinimumThreshold() { return m_MinimumThreshold; } std::ptrdiff_t GetMinimumIndex() { return m_MinimumIndex; } vigra::ArrayVector* GetBestCurrentCounts() { return m_BestCurrentCounts; } private: bool m_UsePointWeights; bool m_UseRandomSplit; WeightContainerType m_PointWeights; // From original code vigra::ArrayVector m_ClassWeights; vigra::ArrayVector m_BestCurrentCounts[2]; double m_MinimumLoss; double m_MinimumThreshold; std::ptrdiff_t m_MinimumIndex; vigra::ProblemSpec<> m_ExtParameter; + AdditionalRFDataAbstract* m_AdditionalData; }; } #include <../src/Splitter/mitkLinearSplitting.cpp> #endif //mitkLinearSplitting_h diff --git a/Modules/Classification/CLVigraRandomForest/include/mitkImpurityLoss.h b/Modules/Classification/CLVigraRandomForest/include/mitkPUImpurityLoss.h similarity index 65% copy from Modules/Classification/CLVigraRandomForest/include/mitkImpurityLoss.h copy to Modules/Classification/CLVigraRandomForest/include/mitkPUImpurityLoss.h index abce530788..5088e7b0d4 100644 --- a/Modules/Classification/CLVigraRandomForest/include/mitkImpurityLoss.h +++ b/Modules/Classification/CLVigraRandomForest/include/mitkPUImpurityLoss.h @@ -1,55 +1,72 @@ -#ifndef mitkImpurityLoss_h -#define mitkImpurityLoss_h +#ifndef mitkPUImpurityLoss_h +#define mitkPUImpurityLoss_h #include #include +#include namespace mitk { + + template + class PURFProblemSpec : vigra::ProblemSpec + { + public: + vigra::ArrayVector kappa_; // if classes have different importance + }; + + template , class TWeightContainer = vigra::MultiArrayView<2, double> > - class ImpurityLoss + class PUImpurityLoss { public: typedef TLabelContainer LabelContainerType; typedef TWeightContainer WeightContainerType; template - ImpurityLoss(TLabelContainer const &labels, - vigra::ProblemSpec const &ext); + PUImpurityLoss(TLabelContainer const &labels, + vigra::ProblemSpec const &ext, + AdditionalRFDataAbstract *data); void Reset(); + void UpdatePUCounts(); + template double Increment(TDataIterator begin, TDataIterator end); template double Decrement(TDataIterator begin, TDataIterator end); template double Init(TArray initCounts); vigra::ArrayVector const& Response(); void UsePointWeights(bool useWeights); bool IsUsingPointWeights(); void SetPointWeights(TWeightContainer weight); WeightContainerType GetPointWeights(); private: bool m_UsePointWeights; TWeightContainer m_PointWeights; //Variable of origin TLabelContainer const& m_Labels; vigra::ArrayVector m_Counts; + vigra::ArrayVector m_PUCounts; + vigra::ArrayVector m_Kappa; vigra::ArrayVector m_ClassWeights; double m_TotalCount; + double m_PUTotalCount; + int m_ClassCount; TLossFunction m_LossFunction; }; } -#include <../src/Splitter/mitkImpurityLoss.cpp> +#include <../src/Splitter/mitkPUImpurityLoss.cpp> #endif //mitkImpurityLoss_h diff --git a/Modules/Classification/CLVigraRandomForest/include/mitkVigraRandomForestClassifier.h b/Modules/Classification/CLVigraRandomForest/include/mitkPURFClassifier.h similarity index 71% copy from Modules/Classification/CLVigraRandomForest/include/mitkVigraRandomForestClassifier.h copy to Modules/Classification/CLVigraRandomForest/include/mitkPURFClassifier.h index 84abe3518f..1a6c02704f 100644 --- a/Modules/Classification/CLVigraRandomForest/include/mitkVigraRandomForestClassifier.h +++ b/Modules/Classification/CLVigraRandomForest/include/mitkPURFClassifier.h @@ -1,93 +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. ===================================================================*/ -#ifndef mitkVigraRandomForestClassifier_h -#define mitkVigraRandomForestClassifier_h +#ifndef mitkPURFClassifier_h +#define mitkPURFClassifier_h #include #include //#include #include #include namespace mitk { - class MITKCLVIGRARANDOMFOREST_EXPORT VigraRandomForestClassifier : public AbstractClassifier + class MITKCLVIGRARANDOMFOREST_EXPORT PURFClassifier : public AbstractClassifier { public: - mitkClassMacro(VigraRandomForestClassifier,AbstractClassifier) + mitkClassMacro(PURFClassifier, AbstractClassifier) itkFactorylessNewMacro(Self) itkCloneMacro(Self) - VigraRandomForestClassifier(); + PURFClassifier(); - ~VigraRandomForestClassifier() override; + ~PURFClassifier(); - void Train(const Eigen::MatrixXd &X, const Eigen::MatrixXi &Y) override; - void OnlineTrain(const Eigen::MatrixXd &X, const Eigen::MatrixXi &Y); - Eigen::MatrixXi Predict(const Eigen::MatrixXd &X) override; + void Train(const Eigen::MatrixXd &X, const Eigen::MatrixXi &Y); + + Eigen::MatrixXi Predict(const Eigen::MatrixXd &X); Eigen::MatrixXi PredictWeighted(const Eigen::MatrixXd &X); - bool SupportsPointWiseWeight() override; - bool SupportsPointWiseProbability() override; + bool SupportsPointWiseWeight(); + bool SupportsPointWiseProbability(); void ConvertParameter(); + vigra::ArrayVector CalculateKappa(const Eigen::MatrixXd & X_in, const Eigen::MatrixXi &Y_in); void SetRandomForest(const vigra::RandomForest & rf); const vigra::RandomForest & GetRandomForest() const; - void UsePointWiseWeight(bool) override; + void UsePointWiseWeight(bool); void SetMaximumTreeDepth(int); void SetMinimumSplitNodeSize(int); void SetPrecision(double); void SetSamplesPerTree(double); void UseSampleWithReplacement(bool); void SetTreeCount(int); void SetWeightLambda(double); - void SetTreeWeights(Eigen::MatrixXd weights); - void SetTreeWeight(int treeId, double weight); - Eigen::MatrixXd GetTreeWeights() const; - void PrintParameter(std::ostream &str = std::cout); + void SetClassProbabilities(Eigen::VectorXd probabilities); + Eigen::VectorXd GetClassProbabilites(); + private: // *------------------- // * THREADING // *------------------- struct TrainingData; struct PredictionData; struct EigenToVigraTransform; struct Parameter; + vigra::MultiArrayView<2, double> m_Probabilities; Eigen::MatrixXd m_TreeWeights; + Eigen::VectorXd m_ClassProbabilities; Parameter * m_Parameter; vigra::RandomForest m_RandomForest; static ITK_THREAD_RETURN_TYPE TrainTreesCallback(void *); static ITK_THREAD_RETURN_TYPE PredictCallback(void *); static ITK_THREAD_RETURN_TYPE PredictWeightedCallback(void *); static void VigraPredictWeighted(PredictionData *data, vigra::MultiArrayView<2, double> & X, vigra::MultiArrayView<2, int> & Y, vigra::MultiArrayView<2, double> & P); }; } -#endif //mitkVigraRandomForestClassifier_h +#endif //mitkPURFClassifier_h diff --git a/Modules/Classification/CLVigraRandomForest/include/mitkThresholdSplit.h b/Modules/Classification/CLVigraRandomForest/include/mitkThresholdSplit.h index 643e26e1ec..62c1d99116 100644 --- a/Modules/Classification/CLVigraRandomForest/include/mitkThresholdSplit.h +++ b/Modules/Classification/CLVigraRandomForest/include/mitkThresholdSplit.h @@ -1,81 +1,86 @@ #ifndef mitkThresholdSplit_h #define mitkThresholdSplit_h #include #include +#include namespace mitk { template class ThresholdSplit: public vigra::SplitBase { public: ThresholdSplit(); // ThresholdSplit(const ThresholdSplit & other); void SetFeatureCalculator(TFeatureCalculator processor); TFeatureCalculator GetFeatureCalculator() const; void SetCalculatingFeature(bool calculate); bool IsCalculatingFeature() const; void UsePointBasedWeights(bool weightsOn); bool IsUsingPointBasedWeights() const; void UseRandomSplit(bool split) {m_UseRandomSplit = split;} bool IsUsingRandomSplit() const { return m_UseRandomSplit; } void SetPrecision(double value); double GetPrecision() const; void SetMaximumTreeDepth(int value); virtual int GetMaximumTreeDepth() const; + void SetAdditionalData(AdditionalRFDataAbstract* data); + AdditionalRFDataAbstract* GetAdditionalData() const; + void SetWeights(vigra::MultiArrayView<2, double> weights); vigra::MultiArrayView<2, double> GetWeights() const; // From vigra::ThresholdSplit double minGini() const; int bestSplitColumn() const; double bestSplitThreshold() const; template void set_external_parameters(vigra::ProblemSpec const & in); template int findBestSplit(vigra::MultiArrayView<2, T, C> features, vigra::MultiArrayView<2, T2, C2> labels, Region & region, vigra::ArrayVector& childRegions, Random & randint); double region_gini_; private: // From vigra::ThresholdSplit typedef vigra::SplitBase SB; // splitter parameters (used by copy constructor) bool m_CalculatingFeature; bool m_UseWeights; bool m_UseRandomSplit; double m_Precision; int m_MaximumTreeDepth; TFeatureCalculator m_FeatureCalculator; vigra::MultiArrayView<2, double> m_Weights; // variabels to work with vigra::ArrayVector splitColumns; TColumnDecisionFunctor bgfunc; vigra::ArrayVector min_gini_; vigra::ArrayVector min_indices_; vigra::ArrayVector min_thresholds_; int bestSplitIndex; + AdditionalRFDataAbstract* m_AdditionalData; }; } #include <../src/Splitter/mitkThresholdSplit.cpp> #endif //mitkThresholdSplit_h diff --git a/Modules/Classification/CLVigraRandomForest/include/mitkVigraRandomForestClassifier.h b/Modules/Classification/CLVigraRandomForest/include/mitkVigraRandomForestClassifier.h index 84abe3518f..6878db61f0 100644 --- a/Modules/Classification/CLVigraRandomForest/include/mitkVigraRandomForestClassifier.h +++ b/Modules/Classification/CLVigraRandomForest/include/mitkVigraRandomForestClassifier.h @@ -1,93 +1,94 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkVigraRandomForestClassifier_h #define mitkVigraRandomForestClassifier_h #include #include //#include #include #include namespace mitk { class MITKCLVIGRARANDOMFOREST_EXPORT VigraRandomForestClassifier : public AbstractClassifier { public: - mitkClassMacro(VigraRandomForestClassifier,AbstractClassifier) + mitkClassMacro(VigraRandomForestClassifier, AbstractClassifier) itkFactorylessNewMacro(Self) itkCloneMacro(Self) - VigraRandomForestClassifier(); + VigraRandomForestClassifier(); ~VigraRandomForestClassifier() override; void Train(const Eigen::MatrixXd &X, const Eigen::MatrixXi &Y) override; void OnlineTrain(const Eigen::MatrixXd &X, const Eigen::MatrixXi &Y); Eigen::MatrixXi Predict(const Eigen::MatrixXd &X) override; Eigen::MatrixXi PredictWeighted(const Eigen::MatrixXd &X); bool SupportsPointWiseWeight() override; bool SupportsPointWiseProbability() override; void ConvertParameter(); void SetRandomForest(const vigra::RandomForest & rf); const vigra::RandomForest & GetRandomForest() const; void UsePointWiseWeight(bool) override; void SetMaximumTreeDepth(int); void SetMinimumSplitNodeSize(int); void SetPrecision(double); void SetSamplesPerTree(double); void UseSampleWithReplacement(bool); void SetTreeCount(int); void SetWeightLambda(double); void SetTreeWeights(Eigen::MatrixXd weights); void SetTreeWeight(int treeId, double weight); Eigen::MatrixXd GetTreeWeights() const; void PrintParameter(std::ostream &str = std::cout); private: // *------------------- // * THREADING // *------------------- struct TrainingData; struct PredictionData; struct EigenToVigraTransform; struct Parameter; + vigra::MultiArrayView<2, double> m_Probabilities; Eigen::MatrixXd m_TreeWeights; Parameter * m_Parameter; vigra::RandomForest m_RandomForest; static ITK_THREAD_RETURN_TYPE TrainTreesCallback(void *); static ITK_THREAD_RETURN_TYPE PredictCallback(void *); static ITK_THREAD_RETURN_TYPE PredictWeightedCallback(void *); static void VigraPredictWeighted(PredictionData *data, vigra::MultiArrayView<2, double> & X, vigra::MultiArrayView<2, int> & Y, vigra::MultiArrayView<2, double> & P); }; } #endif //mitkVigraRandomForestClassifier_h diff --git a/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkVigraRandomForestClassifier.cpp b/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkPURFClassifier.cpp similarity index 64% copy from Modules/Classification/CLVigraRandomForest/src/Classifier/mitkVigraRandomForestClassifier.cpp copy to Modules/Classification/CLVigraRandomForest/src/Classifier/mitkPURFClassifier.cpp index 150dd5417f..1210f113ca 100644 --- a/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkVigraRandomForestClassifier.cpp +++ b/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkPURFClassifier.cpp @@ -1,592 +1,478 @@ /*=================================================================== 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. ===================================================================*/ // MITK includes -#include +#include #include +#include #include #include #include // Vigra includes #include #include // ITK include #include #include #include -typedef mitk::ThresholdSplit >,int,vigra::ClassificationTag> DefaultSplitType; +typedef mitk::ThresholdSplit >,int,vigra::ClassificationTag> DefaultPUSplitType; -struct mitk::VigraRandomForestClassifier::Parameter +struct mitk::PURFClassifier::Parameter { vigra::RF_OptionTag Stratification; bool SampleWithReplacement; bool UseRandomSplit; bool UsePointBasedWeights; int TreeCount; int MinimumSplitNodeSize; int TreeDepth; double Precision; double WeightLambda; double SamplesPerTree; }; -struct mitk::VigraRandomForestClassifier::TrainingData +struct mitk::PURFClassifier::TrainingData { TrainingData(unsigned int numberOfTrees, const vigra::RandomForest & refRF, - const DefaultSplitType & refSplitter, + const DefaultPUSplitType & refSplitter, const vigra::MultiArrayView<2, double> refFeature, const vigra::MultiArrayView<2, int> refLabel, const Parameter parameter) : m_ClassCount(0), m_NumberOfTrees(numberOfTrees), m_RandomForest(refRF), m_Splitter(refSplitter), m_Feature(refFeature), m_Label(refLabel), m_Parameter(parameter) { m_mutex = itk::FastMutexLock::New(); } vigra::ArrayVector::DecisionTree_t> trees_; int m_ClassCount; unsigned int m_NumberOfTrees; const vigra::RandomForest & m_RandomForest; - const DefaultSplitType & m_Splitter; + const DefaultPUSplitType & m_Splitter; const vigra::MultiArrayView<2, double> m_Feature; const vigra::MultiArrayView<2, int> m_Label; itk::FastMutexLock::Pointer m_mutex; Parameter m_Parameter; }; -struct mitk::VigraRandomForestClassifier::PredictionData +struct mitk::PURFClassifier::PredictionData { PredictionData(const vigra::RandomForest & refRF, const vigra::MultiArrayView<2, double> refFeature, vigra::MultiArrayView<2, int> refLabel, vigra::MultiArrayView<2, double> refProb, vigra::MultiArrayView<2, double> refTreeWeights) : m_RandomForest(refRF), m_Feature(refFeature), m_Label(refLabel), m_Probabilities(refProb), m_TreeWeights(refTreeWeights) { } const vigra::RandomForest & m_RandomForest; const vigra::MultiArrayView<2, double> m_Feature; vigra::MultiArrayView<2, int> m_Label; vigra::MultiArrayView<2, double> m_Probabilities; vigra::MultiArrayView<2, double> m_TreeWeights; }; -mitk::VigraRandomForestClassifier::VigraRandomForestClassifier() +mitk::PURFClassifier::PURFClassifier() :m_Parameter(nullptr) { - itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); - command->SetCallbackFunction(this, &mitk::VigraRandomForestClassifier::ConvertParameter); + itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); + command->SetCallbackFunction(this, &mitk::PURFClassifier::ConvertParameter); this->GetPropertyList()->AddObserver( itk::ModifiedEvent(), command ); } -mitk::VigraRandomForestClassifier::~VigraRandomForestClassifier() +mitk::PURFClassifier::~PURFClassifier() { } -bool mitk::VigraRandomForestClassifier::SupportsPointWiseWeight() +void mitk::PURFClassifier::SetClassProbabilities(Eigen::VectorXd probabilities) +{ + m_ClassProbabilities = probabilities; +} + +Eigen::VectorXd mitk::PURFClassifier::GetClassProbabilites() +{ + return m_ClassProbabilities; +} + +bool mitk::PURFClassifier::SupportsPointWiseWeight() { return true; } -bool mitk::VigraRandomForestClassifier::SupportsPointWiseProbability() +bool mitk::PURFClassifier::SupportsPointWiseProbability() { return true; } -void mitk::VigraRandomForestClassifier::OnlineTrain(const Eigen::MatrixXd & X_in, const Eigen::MatrixXi &Y_in) + +vigra::ArrayVector mitk::PURFClassifier::CalculateKappa(const Eigen::MatrixXd & /* X_in */, const Eigen::MatrixXi & Y_in) { - vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data()); - vigra::MultiArrayView<2, int> Y(vigra::Shape2(Y_in.rows(),Y_in.cols()),Y_in.data()); - m_RandomForest.onlineLearn(X,Y,0,true); + int maximumValue = Y_in.maxCoeff(); + vigra::ArrayVector kappa(maximumValue + 1); + vigra::ArrayVector counts(maximumValue + 1); + for (int i = 0; i < Y_in.rows(); ++i) + { + counts[Y_in(i, 0)] += 1; + } + for (int i = 0; i < maximumValue+1; ++i) + { + if (counts[i] > 0) + { + kappa[i] = counts[0] * m_ClassProbabilities[i] / counts[i] + 1; + } + else + { + kappa[i] = 1; + } + } + return kappa; } -void mitk::VigraRandomForestClassifier::Train(const Eigen::MatrixXd & X_in, const Eigen::MatrixXi &Y_in) + +void mitk::PURFClassifier::Train(const Eigen::MatrixXd & X_in, const Eigen::MatrixXi &Y_in) { this->ConvertParameter(); - DefaultSplitType splitter; + PURFData* purfData = new PURFData; + purfData->m_Kappa = this->CalculateKappa(X_in, Y_in); + + DefaultPUSplitType splitter; splitter.UsePointBasedWeights(m_Parameter->UsePointBasedWeights); splitter.UseRandomSplit(m_Parameter->UseRandomSplit); splitter.SetPrecision(m_Parameter->Precision); splitter.SetMaximumTreeDepth(m_Parameter->TreeDepth); + splitter.SetAdditionalData(purfData); // Weights handled as member variable if (m_Parameter->UsePointBasedWeights) { // Set influence of the weight (0 no influenc to 1 max influence) this->m_PointWiseWeight.unaryExpr([this](double t){ return std::pow(t, this->m_Parameter->WeightLambda) ;}); vigra::MultiArrayView<2, double> W(vigra::Shape2(this->m_PointWiseWeight.rows(),this->m_PointWiseWeight.cols()),this->m_PointWiseWeight.data()); splitter.SetWeights(W); } vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data()); vigra::MultiArrayView<2, int> Y(vigra::Shape2(Y_in.rows(),Y_in.cols()),Y_in.data()); m_RandomForest.set_options().tree_count(1); // Number of trees that are calculated; m_RandomForest.set_options().use_stratification(m_Parameter->Stratification); m_RandomForest.set_options().sample_with_replacement(m_Parameter->SampleWithReplacement); m_RandomForest.set_options().samples_per_tree(m_Parameter->SamplesPerTree); m_RandomForest.set_options().min_split_node_size(m_Parameter->MinimumSplitNodeSize); m_RandomForest.learn(X, Y,vigra::rf::visitors::VisitorBase(),splitter); std::unique_ptr data(new TrainingData(m_Parameter->TreeCount,m_RandomForest,splitter,X,Y, *m_Parameter)); itk::MultiThreader::Pointer threader = itk::MultiThreader::New(); threader->SetSingleMethod(this->TrainTreesCallback,data.get()); threader->SingleMethodExecute(); // set result trees m_RandomForest.set_options().tree_count(m_Parameter->TreeCount); m_RandomForest.ext_param_.class_count_ = data->m_ClassCount; m_RandomForest.trees_ = data->trees_; // Set Tree Weights to default m_TreeWeights = Eigen::MatrixXd(m_Parameter->TreeCount,1); m_TreeWeights.fill(1.0); + delete purfData; } -Eigen::MatrixXi mitk::VigraRandomForestClassifier::Predict(const Eigen::MatrixXd &X_in) -{ - // Initialize output Eigen matrices - m_OutProbability = Eigen::MatrixXd(X_in.rows(),m_RandomForest.class_count()); - m_OutProbability.fill(0); - m_OutLabel = Eigen::MatrixXi(X_in.rows(),1); - m_OutLabel.fill(0); - - // If no weights provided - if(m_TreeWeights.rows() != m_RandomForest.tree_count()) - { - m_TreeWeights = Eigen::MatrixXd(m_RandomForest.tree_count(),1); - m_TreeWeights.fill(1); - } - - - vigra::MultiArrayView<2, double> P(vigra::Shape2(m_OutProbability.rows(),m_OutProbability.cols()),m_OutProbability.data()); - vigra::MultiArrayView<2, int> Y(vigra::Shape2(m_OutLabel.rows(),m_OutLabel.cols()),m_OutLabel.data()); - vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data()); - vigra::MultiArrayView<2, double> TW(vigra::Shape2(m_RandomForest.tree_count(),1),m_TreeWeights.data()); - - std::unique_ptr data; - data.reset( new PredictionData(m_RandomForest,X,Y,P,TW)); - - itk::MultiThreader::Pointer threader = itk::MultiThreader::New(); - threader->SetSingleMethod(this->PredictCallback,data.get()); - threader->SingleMethodExecute(); - - return m_OutLabel; -} - -Eigen::MatrixXi mitk::VigraRandomForestClassifier::PredictWeighted(const Eigen::MatrixXd &X_in) +Eigen::MatrixXi mitk::PURFClassifier::Predict(const Eigen::MatrixXd &X_in) { // Initialize output Eigen matrices m_OutProbability = Eigen::MatrixXd(X_in.rows(),m_RandomForest.class_count()); m_OutProbability.fill(0); m_OutLabel = Eigen::MatrixXi(X_in.rows(),1); m_OutLabel.fill(0); // If no weights provided if(m_TreeWeights.rows() != m_RandomForest.tree_count()) { m_TreeWeights = Eigen::MatrixXd(m_RandomForest.tree_count(),1); m_TreeWeights.fill(1); } - vigra::MultiArrayView<2, double> P(vigra::Shape2(m_OutProbability.rows(),m_OutProbability.cols()),m_OutProbability.data()); vigra::MultiArrayView<2, int> Y(vigra::Shape2(m_OutLabel.rows(),m_OutLabel.cols()),m_OutLabel.data()); vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data()); vigra::MultiArrayView<2, double> TW(vigra::Shape2(m_RandomForest.tree_count(),1),m_TreeWeights.data()); std::unique_ptr data; - data.reset( new PredictionData(m_RandomForest,X,Y,P,TW)); + data.reset(new PredictionData(m_RandomForest, X, Y, P, TW)); itk::MultiThreader::Pointer threader = itk::MultiThreader::New(); - threader->SetSingleMethod(this->PredictWeightedCallback,data.get()); + threader->SetSingleMethod(this->PredictCallback, data.get()); threader->SingleMethodExecute(); + m_Probabilities = data->m_Probabilities; return m_OutLabel; } - - -void mitk::VigraRandomForestClassifier::SetTreeWeights(Eigen::MatrixXd weights) -{ - m_TreeWeights = weights; -} - -Eigen::MatrixXd mitk::VigraRandomForestClassifier::GetTreeWeights() const -{ - return m_TreeWeights; -} - -ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::TrainTreesCallback(void * arg) +ITK_THREAD_RETURN_TYPE mitk::PURFClassifier::TrainTreesCallback(void * arg) { // Get the ThreadInfoStruct typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType; ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg ); TrainingData * data = (TrainingData *)(infoStruct->UserData); unsigned int numberOfTreesToCalculate = 0; // define the number of tress the forest have to calculate numberOfTreesToCalculate = data->m_NumberOfTrees / infoStruct->NumberOfThreads; // the 0th thread takes the residuals if(infoStruct->ThreadID == 0) numberOfTreesToCalculate += data->m_NumberOfTrees % infoStruct->NumberOfThreads; if(numberOfTreesToCalculate != 0){ // Copy the Treestructure defined in userData vigra::RandomForest rf = data->m_RandomForest; // Initialize a splitter for the leraning process - DefaultSplitType splitter; + DefaultPUSplitType splitter; splitter.UsePointBasedWeights(data->m_Splitter.IsUsingPointBasedWeights()); splitter.UseRandomSplit(data->m_Splitter.IsUsingRandomSplit()); splitter.SetPrecision(data->m_Splitter.GetPrecision()); splitter.SetMaximumTreeDepth(data->m_Splitter.GetMaximumTreeDepth()); splitter.SetWeights(data->m_Splitter.GetWeights()); + splitter.SetAdditionalData(data->m_Splitter.GetAdditionalData()); rf.trees_.clear(); rf.set_options().tree_count(numberOfTreesToCalculate); rf.set_options().use_stratification(data->m_Parameter.Stratification); rf.set_options().sample_with_replacement(data->m_Parameter.SampleWithReplacement); rf.set_options().samples_per_tree(data->m_Parameter.SamplesPerTree); rf.set_options().min_split_node_size(data->m_Parameter.MinimumSplitNodeSize); rf.learn(data->m_Feature, data->m_Label,vigra::rf::visitors::VisitorBase(),splitter); data->m_mutex->Lock(); for(const auto & tree : rf.trees_) data->trees_.push_back(tree); data->m_ClassCount = rf.class_count(); data->m_mutex->Unlock(); } - return 0; + return NULL; } -ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::PredictCallback(void * arg) +ITK_THREAD_RETURN_TYPE mitk::PURFClassifier::PredictCallback(void * arg) { // Get the ThreadInfoStruct typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType; ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg ); // assigne the thread id const unsigned int threadId = infoStruct->ThreadID; // Get the user defined parameters containing all // neccesary informations PredictionData * data = (PredictionData *)(infoStruct->UserData); unsigned int numberOfRowsToCalculate = 0; // Get number of rows to calculate numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfThreads; unsigned int start_index = numberOfRowsToCalculate * threadId; unsigned int end_index = numberOfRowsToCalculate * (threadId+1); // the last thread takes the residuals if(threadId == infoStruct->NumberOfThreads-1) { end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfThreads; } vigra::MultiArrayView<2, double> split_features; vigra::MultiArrayView<2, int> split_labels; vigra::MultiArrayView<2, double> split_probability; { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index,data->m_Feature.shape(1)); split_features = data->m_Feature.subarray(lowerBound,upperBound); } { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index, data->m_Label.shape(1)); split_labels = data->m_Label.subarray(lowerBound,upperBound); } { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index,data->m_Probabilities.shape(1)); split_probability = data->m_Probabilities.subarray(lowerBound,upperBound); } data->m_RandomForest.predictLabels(split_features,split_labels); data->m_RandomForest.predictProbabilities(split_features, split_probability); - return 0; + return NULL; } -ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::PredictWeightedCallback(void * arg) -{ - // Get the ThreadInfoStruct - typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType; - ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg ); - // assigne the thread id - const unsigned int threadId = infoStruct->ThreadID; - - // Get the user defined parameters containing all - // neccesary informations - PredictionData * data = (PredictionData *)(infoStruct->UserData); - unsigned int numberOfRowsToCalculate = 0; - - // Get number of rows to calculate - numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfThreads; - - unsigned int start_index = numberOfRowsToCalculate * threadId; - unsigned int end_index = numberOfRowsToCalculate * (threadId+1); - - // the last thread takes the residuals - if(threadId == infoStruct->NumberOfThreads-1) { - end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfThreads; - } - - vigra::MultiArrayView<2, double> split_features; - vigra::MultiArrayView<2, int> split_labels; - vigra::MultiArrayView<2, double> split_probability; - { - vigra::TinyVector lowerBound(start_index,0); - vigra::TinyVector upperBound(end_index,data->m_Feature.shape(1)); - split_features = data->m_Feature.subarray(lowerBound,upperBound); - } - - { - vigra::TinyVector lowerBound(start_index,0); - vigra::TinyVector upperBound(end_index, data->m_Label.shape(1)); - split_labels = data->m_Label.subarray(lowerBound,upperBound); - } - - { - vigra::TinyVector lowerBound(start_index,0); - vigra::TinyVector upperBound(end_index,data->m_Probabilities.shape(1)); - split_probability = data->m_Probabilities.subarray(lowerBound,upperBound); - } - - VigraPredictWeighted(data, split_features,split_labels,split_probability); - - return 0; -} - - -void mitk::VigraRandomForestClassifier::VigraPredictWeighted(PredictionData * data, vigra::MultiArrayView<2, double> & X, vigra::MultiArrayView<2, int> & Y, vigra::MultiArrayView<2, double> & P) -{ - - int isSampleWeighted = data->m_RandomForest.options_.predict_weighted_; -//#pragma omp parallel for - for(int row=0; row < vigra::rowCount(X); ++row) - { - vigra::MultiArrayView<2, double, vigra::StridedArrayTag> currentRow(rowVector(X, row)); - - vigra::ArrayVector::const_iterator weights; - - //totalWeight == totalVoteCount! - double totalWeight = 0.0; - - //Let each tree classify... - for(int k=0; km_RandomForest.options_.tree_count_; ++k) - { - //get weights predicted by single tree - weights = data->m_RandomForest.trees_[k /*tree_indices_[k]*/].predict(currentRow); - double numberOfLeafObservations = (*(weights-1)); - - //update votecount. - for(int l=0; lm_RandomForest.ext_param_.class_count_; ++l) - { - // Either the original weights are taken or the tree is additional weighted by the number of Observations in the leaf node. - double cur_w = weights[l] * (isSampleWeighted * numberOfLeafObservations + (1-isSampleWeighted)); - cur_w = cur_w * data->m_TreeWeights(k,0); - P(row, l) += (int)cur_w; - //every weight in totalWeight. - totalWeight += cur_w; - } - } - - //Normalise votes in each row by total VoteCount (totalWeight - for(int l=0; l< data->m_RandomForest.ext_param_.class_count_; ++l) - { - P(row, l) /= vigra::detail::RequiresExplicitCast::cast(totalWeight); - } - int erg; - int maxCol = 0; - for (int col=0;colm_RandomForest.class_count();++col) - { - if (data->m_Probabilities(row,col) > data->m_Probabilities(row, maxCol)) - maxCol = col; - } - data->m_RandomForest.ext_param_.to_classlabel(maxCol, erg); - Y(row,0) = erg; - } -} - -void mitk::VigraRandomForestClassifier::ConvertParameter() +void mitk::PURFClassifier::ConvertParameter() { if(this->m_Parameter == nullptr) this->m_Parameter = new Parameter(); // Get the proerty // Some defaults - MITK_INFO("VigraRandomForestClassifier") << "Convert Parameter"; + MITK_INFO("PURFClassifier") << "Convert Parameter"; if(!this->GetPropertyList()->Get("usepointbasedweight",this->m_Parameter->UsePointBasedWeights)) this->m_Parameter->UsePointBasedWeights = false; if(!this->GetPropertyList()->Get("userandomsplit",this->m_Parameter->UseRandomSplit)) this->m_Parameter->UseRandomSplit = false; if(!this->GetPropertyList()->Get("treedepth",this->m_Parameter->TreeDepth)) this->m_Parameter->TreeDepth = 20; if(!this->GetPropertyList()->Get("treecount",this->m_Parameter->TreeCount)) this->m_Parameter->TreeCount = 100; if(!this->GetPropertyList()->Get("minimalsplitnodesize",this->m_Parameter->MinimumSplitNodeSize)) this->m_Parameter->MinimumSplitNodeSize = 5; if(!this->GetPropertyList()->Get("precision",this->m_Parameter->Precision)) this->m_Parameter->Precision = mitk::eps; if(!this->GetPropertyList()->Get("samplespertree",this->m_Parameter->SamplesPerTree)) this->m_Parameter->SamplesPerTree = 0.6; if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->SampleWithReplacement)) this->m_Parameter->SampleWithReplacement = true; if(!this->GetPropertyList()->Get("lambda",this->m_Parameter->WeightLambda)) this->m_Parameter->WeightLambda = 1.0; // Not used yet // if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->Stratification)) this->m_Parameter->Stratification = vigra::RF_NONE; // no Property given } -void mitk::VigraRandomForestClassifier::PrintParameter(std::ostream & str) +void mitk::PURFClassifier::PrintParameter(std::ostream & str) { if(this->m_Parameter == nullptr) { - MITK_WARN("VigraRandomForestClassifier") << "Parameters are not initialized. Please call ConvertParameter() first!"; + MITK_WARN("PURFClassifier") << "Parameters are not initialized. Please call ConvertParameter() first!"; return; } this->ConvertParameter(); // Get the proerty // Some defaults if(!this->GetPropertyList()->Get("usepointbasedweight",this->m_Parameter->UsePointBasedWeights)) str << "usepointbasedweight\tNOT SET (default " << this->m_Parameter->UsePointBasedWeights << ")" << "\n"; else str << "usepointbasedweight\t" << this->m_Parameter->UsePointBasedWeights << "\n"; if(!this->GetPropertyList()->Get("userandomsplit",this->m_Parameter->UseRandomSplit)) str << "userandomsplit\tNOT SET (default " << this->m_Parameter->UseRandomSplit << ")" << "\n"; else str << "userandomsplit\t" << this->m_Parameter->UseRandomSplit << "\n"; if(!this->GetPropertyList()->Get("treedepth",this->m_Parameter->TreeDepth)) str << "treedepth\t\tNOT SET (default " << this->m_Parameter->TreeDepth << ")" << "\n"; else str << "treedepth\t\t" << this->m_Parameter->TreeDepth << "\n"; if(!this->GetPropertyList()->Get("minimalsplitnodesize",this->m_Parameter->MinimumSplitNodeSize)) str << "minimalsplitnodesize\tNOT SET (default " << this->m_Parameter->MinimumSplitNodeSize << ")" << "\n"; else str << "minimalsplitnodesize\t" << this->m_Parameter->MinimumSplitNodeSize << "\n"; if(!this->GetPropertyList()->Get("precision",this->m_Parameter->Precision)) str << "precision\t\tNOT SET (default " << this->m_Parameter->Precision << ")" << "\n"; else str << "precision\t\t" << this->m_Parameter->Precision << "\n"; if(!this->GetPropertyList()->Get("samplespertree",this->m_Parameter->SamplesPerTree)) str << "samplespertree\tNOT SET (default " << this->m_Parameter->SamplesPerTree << ")" << "\n"; else str << "samplespertree\t" << this->m_Parameter->SamplesPerTree << "\n"; if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->SampleWithReplacement)) str << "samplewithreplacement\tNOT SET (default " << this->m_Parameter->SampleWithReplacement << ")" << "\n"; else str << "samplewithreplacement\t" << this->m_Parameter->SampleWithReplacement << "\n"; if(!this->GetPropertyList()->Get("treecount",this->m_Parameter->TreeCount)) str << "treecount\t\tNOT SET (default " << this->m_Parameter->TreeCount << ")" << "\n"; else str << "treecount\t\t" << this->m_Parameter->TreeCount << "\n"; if(!this->GetPropertyList()->Get("lambda",this->m_Parameter->WeightLambda)) str << "lambda\t\tNOT SET (default " << this->m_Parameter->WeightLambda << ")" << "\n"; else str << "lambda\t\t" << this->m_Parameter->WeightLambda << "\n"; // if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->Stratification)) // this->m_Parameter->Stratification = vigra:RF_NONE; // no Property given } -void mitk::VigraRandomForestClassifier::UsePointWiseWeight(bool val) +void mitk::PURFClassifier::UsePointWiseWeight(bool val) { mitk::AbstractClassifier::UsePointWiseWeight(val); this->GetPropertyList()->SetBoolProperty("usepointbasedweight",val); } -void mitk::VigraRandomForestClassifier::SetMaximumTreeDepth(int val) +void mitk::PURFClassifier::SetMaximumTreeDepth(int val) { this->GetPropertyList()->SetIntProperty("treedepth",val); } -void mitk::VigraRandomForestClassifier::SetMinimumSplitNodeSize(int val) +void mitk::PURFClassifier::SetMinimumSplitNodeSize(int val) { this->GetPropertyList()->SetIntProperty("minimalsplitnodesize",val); } -void mitk::VigraRandomForestClassifier::SetPrecision(double val) +void mitk::PURFClassifier::SetPrecision(double val) { this->GetPropertyList()->SetDoubleProperty("precision",val); } -void mitk::VigraRandomForestClassifier::SetSamplesPerTree(double val) +void mitk::PURFClassifier::SetSamplesPerTree(double val) { this->GetPropertyList()->SetDoubleProperty("samplespertree",val); } -void mitk::VigraRandomForestClassifier::UseSampleWithReplacement(bool val) +void mitk::PURFClassifier::UseSampleWithReplacement(bool val) { this->GetPropertyList()->SetBoolProperty("samplewithreplacement",val); } -void mitk::VigraRandomForestClassifier::SetTreeCount(int val) +void mitk::PURFClassifier::SetTreeCount(int val) { this->GetPropertyList()->SetIntProperty("treecount",val); } -void mitk::VigraRandomForestClassifier::SetWeightLambda(double val) +void mitk::PURFClassifier::SetWeightLambda(double val) { this->GetPropertyList()->SetDoubleProperty("lambda",val); } -void mitk::VigraRandomForestClassifier::SetTreeWeight(int treeId, double weight) -{ - m_TreeWeights(treeId,0) = weight; -} - -void mitk::VigraRandomForestClassifier::SetRandomForest(const vigra::RandomForest & rf) +void mitk::PURFClassifier::SetRandomForest(const vigra::RandomForest & rf) { this->SetMaximumTreeDepth(rf.ext_param().max_tree_depth); this->SetMinimumSplitNodeSize(rf.options().min_split_node_size_); this->SetTreeCount(rf.options().tree_count_); this->SetSamplesPerTree(rf.options().training_set_proportion_); this->UseSampleWithReplacement(rf.options().sample_with_replacement_); this->m_RandomForest = rf; } -const vigra::RandomForest & mitk::VigraRandomForestClassifier::GetRandomForest() const +const vigra::RandomForest & mitk::PURFClassifier::GetRandomForest() const { return this->m_RandomForest; } diff --git a/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkVigraRandomForestClassifier.cpp b/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkVigraRandomForestClassifier.cpp index 150dd5417f..d0686c944d 100644 --- a/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkVigraRandomForestClassifier.cpp +++ b/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkVigraRandomForestClassifier.cpp @@ -1,592 +1,593 @@ /*=================================================================== 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. ===================================================================*/ // MITK includes #include #include #include #include #include // Vigra includes #include #include // ITK include #include #include #include typedef mitk::ThresholdSplit >,int,vigra::ClassificationTag> DefaultSplitType; struct mitk::VigraRandomForestClassifier::Parameter { vigra::RF_OptionTag Stratification; bool SampleWithReplacement; bool UseRandomSplit; bool UsePointBasedWeights; int TreeCount; int MinimumSplitNodeSize; int TreeDepth; double Precision; double WeightLambda; double SamplesPerTree; }; struct mitk::VigraRandomForestClassifier::TrainingData { TrainingData(unsigned int numberOfTrees, const vigra::RandomForest & refRF, const DefaultSplitType & refSplitter, const vigra::MultiArrayView<2, double> refFeature, const vigra::MultiArrayView<2, int> refLabel, const Parameter parameter) : m_ClassCount(0), m_NumberOfTrees(numberOfTrees), m_RandomForest(refRF), m_Splitter(refSplitter), m_Feature(refFeature), m_Label(refLabel), m_Parameter(parameter) { m_mutex = itk::FastMutexLock::New(); } vigra::ArrayVector::DecisionTree_t> trees_; int m_ClassCount; unsigned int m_NumberOfTrees; const vigra::RandomForest & m_RandomForest; const DefaultSplitType & m_Splitter; const vigra::MultiArrayView<2, double> m_Feature; const vigra::MultiArrayView<2, int> m_Label; itk::FastMutexLock::Pointer m_mutex; Parameter m_Parameter; }; struct mitk::VigraRandomForestClassifier::PredictionData { PredictionData(const vigra::RandomForest & refRF, const vigra::MultiArrayView<2, double> refFeature, vigra::MultiArrayView<2, int> refLabel, vigra::MultiArrayView<2, double> refProb, vigra::MultiArrayView<2, double> refTreeWeights) : m_RandomForest(refRF), m_Feature(refFeature), m_Label(refLabel), m_Probabilities(refProb), m_TreeWeights(refTreeWeights) { } const vigra::RandomForest & m_RandomForest; const vigra::MultiArrayView<2, double> m_Feature; vigra::MultiArrayView<2, int> m_Label; vigra::MultiArrayView<2, double> m_Probabilities; vigra::MultiArrayView<2, double> m_TreeWeights; }; mitk::VigraRandomForestClassifier::VigraRandomForestClassifier() :m_Parameter(nullptr) { itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &mitk::VigraRandomForestClassifier::ConvertParameter); this->GetPropertyList()->AddObserver( itk::ModifiedEvent(), command ); } mitk::VigraRandomForestClassifier::~VigraRandomForestClassifier() { } bool mitk::VigraRandomForestClassifier::SupportsPointWiseWeight() { return true; } bool mitk::VigraRandomForestClassifier::SupportsPointWiseProbability() { return true; } void mitk::VigraRandomForestClassifier::OnlineTrain(const Eigen::MatrixXd & X_in, const Eigen::MatrixXi &Y_in) { vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data()); vigra::MultiArrayView<2, int> Y(vigra::Shape2(Y_in.rows(),Y_in.cols()),Y_in.data()); m_RandomForest.onlineLearn(X,Y,0,true); } void mitk::VigraRandomForestClassifier::Train(const Eigen::MatrixXd & X_in, const Eigen::MatrixXi &Y_in) { this->ConvertParameter(); DefaultSplitType splitter; splitter.UsePointBasedWeights(m_Parameter->UsePointBasedWeights); splitter.UseRandomSplit(m_Parameter->UseRandomSplit); splitter.SetPrecision(m_Parameter->Precision); splitter.SetMaximumTreeDepth(m_Parameter->TreeDepth); // Weights handled as member variable if (m_Parameter->UsePointBasedWeights) { // Set influence of the weight (0 no influenc to 1 max influence) this->m_PointWiseWeight.unaryExpr([this](double t){ return std::pow(t, this->m_Parameter->WeightLambda) ;}); vigra::MultiArrayView<2, double> W(vigra::Shape2(this->m_PointWiseWeight.rows(),this->m_PointWiseWeight.cols()),this->m_PointWiseWeight.data()); splitter.SetWeights(W); } vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data()); vigra::MultiArrayView<2, int> Y(vigra::Shape2(Y_in.rows(),Y_in.cols()),Y_in.data()); m_RandomForest.set_options().tree_count(1); // Number of trees that are calculated; m_RandomForest.set_options().use_stratification(m_Parameter->Stratification); m_RandomForest.set_options().sample_with_replacement(m_Parameter->SampleWithReplacement); m_RandomForest.set_options().samples_per_tree(m_Parameter->SamplesPerTree); m_RandomForest.set_options().min_split_node_size(m_Parameter->MinimumSplitNodeSize); m_RandomForest.learn(X, Y,vigra::rf::visitors::VisitorBase(),splitter); std::unique_ptr data(new TrainingData(m_Parameter->TreeCount,m_RandomForest,splitter,X,Y, *m_Parameter)); itk::MultiThreader::Pointer threader = itk::MultiThreader::New(); threader->SetSingleMethod(this->TrainTreesCallback,data.get()); threader->SingleMethodExecute(); // set result trees m_RandomForest.set_options().tree_count(m_Parameter->TreeCount); m_RandomForest.ext_param_.class_count_ = data->m_ClassCount; m_RandomForest.trees_ = data->trees_; // Set Tree Weights to default m_TreeWeights = Eigen::MatrixXd(m_Parameter->TreeCount,1); m_TreeWeights.fill(1.0); } Eigen::MatrixXi mitk::VigraRandomForestClassifier::Predict(const Eigen::MatrixXd &X_in) { // Initialize output Eigen matrices m_OutProbability = Eigen::MatrixXd(X_in.rows(),m_RandomForest.class_count()); m_OutProbability.fill(0); m_OutLabel = Eigen::MatrixXi(X_in.rows(),1); m_OutLabel.fill(0); // If no weights provided if(m_TreeWeights.rows() != m_RandomForest.tree_count()) { m_TreeWeights = Eigen::MatrixXd(m_RandomForest.tree_count(),1); m_TreeWeights.fill(1); } vigra::MultiArrayView<2, double> P(vigra::Shape2(m_OutProbability.rows(),m_OutProbability.cols()),m_OutProbability.data()); vigra::MultiArrayView<2, int> Y(vigra::Shape2(m_OutLabel.rows(),m_OutLabel.cols()),m_OutLabel.data()); vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data()); vigra::MultiArrayView<2, double> TW(vigra::Shape2(m_RandomForest.tree_count(),1),m_TreeWeights.data()); std::unique_ptr data; - data.reset( new PredictionData(m_RandomForest,X,Y,P,TW)); + data.reset(new PredictionData(m_RandomForest, X, Y, P, TW)); itk::MultiThreader::Pointer threader = itk::MultiThreader::New(); - threader->SetSingleMethod(this->PredictCallback,data.get()); + threader->SetSingleMethod(this->PredictCallback, data.get()); threader->SingleMethodExecute(); + m_Probabilities = data->m_Probabilities; return m_OutLabel; } Eigen::MatrixXi mitk::VigraRandomForestClassifier::PredictWeighted(const Eigen::MatrixXd &X_in) { // Initialize output Eigen matrices m_OutProbability = Eigen::MatrixXd(X_in.rows(),m_RandomForest.class_count()); m_OutProbability.fill(0); m_OutLabel = Eigen::MatrixXi(X_in.rows(),1); m_OutLabel.fill(0); // If no weights provided if(m_TreeWeights.rows() != m_RandomForest.tree_count()) { m_TreeWeights = Eigen::MatrixXd(m_RandomForest.tree_count(),1); m_TreeWeights.fill(1); } vigra::MultiArrayView<2, double> P(vigra::Shape2(m_OutProbability.rows(),m_OutProbability.cols()),m_OutProbability.data()); vigra::MultiArrayView<2, int> Y(vigra::Shape2(m_OutLabel.rows(),m_OutLabel.cols()),m_OutLabel.data()); vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data()); vigra::MultiArrayView<2, double> TW(vigra::Shape2(m_RandomForest.tree_count(),1),m_TreeWeights.data()); std::unique_ptr data; data.reset( new PredictionData(m_RandomForest,X,Y,P,TW)); itk::MultiThreader::Pointer threader = itk::MultiThreader::New(); threader->SetSingleMethod(this->PredictWeightedCallback,data.get()); threader->SingleMethodExecute(); return m_OutLabel; } void mitk::VigraRandomForestClassifier::SetTreeWeights(Eigen::MatrixXd weights) { m_TreeWeights = weights; } Eigen::MatrixXd mitk::VigraRandomForestClassifier::GetTreeWeights() const { return m_TreeWeights; } ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::TrainTreesCallback(void * arg) { // Get the ThreadInfoStruct typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType; ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg ); TrainingData * data = (TrainingData *)(infoStruct->UserData); unsigned int numberOfTreesToCalculate = 0; // define the number of tress the forest have to calculate numberOfTreesToCalculate = data->m_NumberOfTrees / infoStruct->NumberOfThreads; // the 0th thread takes the residuals if(infoStruct->ThreadID == 0) numberOfTreesToCalculate += data->m_NumberOfTrees % infoStruct->NumberOfThreads; if(numberOfTreesToCalculate != 0){ // Copy the Treestructure defined in userData vigra::RandomForest rf = data->m_RandomForest; // Initialize a splitter for the leraning process DefaultSplitType splitter; splitter.UsePointBasedWeights(data->m_Splitter.IsUsingPointBasedWeights()); splitter.UseRandomSplit(data->m_Splitter.IsUsingRandomSplit()); splitter.SetPrecision(data->m_Splitter.GetPrecision()); splitter.SetMaximumTreeDepth(data->m_Splitter.GetMaximumTreeDepth()); splitter.SetWeights(data->m_Splitter.GetWeights()); rf.trees_.clear(); rf.set_options().tree_count(numberOfTreesToCalculate); rf.set_options().use_stratification(data->m_Parameter.Stratification); rf.set_options().sample_with_replacement(data->m_Parameter.SampleWithReplacement); rf.set_options().samples_per_tree(data->m_Parameter.SamplesPerTree); rf.set_options().min_split_node_size(data->m_Parameter.MinimumSplitNodeSize); rf.learn(data->m_Feature, data->m_Label,vigra::rf::visitors::VisitorBase(),splitter); data->m_mutex->Lock(); for(const auto & tree : rf.trees_) data->trees_.push_back(tree); data->m_ClassCount = rf.class_count(); data->m_mutex->Unlock(); } return 0; } ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::PredictCallback(void * arg) { // Get the ThreadInfoStruct typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType; ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg ); // assigne the thread id const unsigned int threadId = infoStruct->ThreadID; // Get the user defined parameters containing all // neccesary informations PredictionData * data = (PredictionData *)(infoStruct->UserData); unsigned int numberOfRowsToCalculate = 0; // Get number of rows to calculate numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfThreads; unsigned int start_index = numberOfRowsToCalculate * threadId; unsigned int end_index = numberOfRowsToCalculate * (threadId+1); // the last thread takes the residuals if(threadId == infoStruct->NumberOfThreads-1) { end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfThreads; } vigra::MultiArrayView<2, double> split_features; vigra::MultiArrayView<2, int> split_labels; vigra::MultiArrayView<2, double> split_probability; { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index,data->m_Feature.shape(1)); split_features = data->m_Feature.subarray(lowerBound,upperBound); } { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index, data->m_Label.shape(1)); split_labels = data->m_Label.subarray(lowerBound,upperBound); } { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index,data->m_Probabilities.shape(1)); split_probability = data->m_Probabilities.subarray(lowerBound,upperBound); } data->m_RandomForest.predictLabels(split_features,split_labels); data->m_RandomForest.predictProbabilities(split_features, split_probability); return 0; } ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::PredictWeightedCallback(void * arg) { // Get the ThreadInfoStruct typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType; ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg ); // assigne the thread id const unsigned int threadId = infoStruct->ThreadID; // Get the user defined parameters containing all // neccesary informations PredictionData * data = (PredictionData *)(infoStruct->UserData); unsigned int numberOfRowsToCalculate = 0; // Get number of rows to calculate numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfThreads; unsigned int start_index = numberOfRowsToCalculate * threadId; unsigned int end_index = numberOfRowsToCalculate * (threadId+1); // the last thread takes the residuals if(threadId == infoStruct->NumberOfThreads-1) { end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfThreads; } vigra::MultiArrayView<2, double> split_features; vigra::MultiArrayView<2, int> split_labels; vigra::MultiArrayView<2, double> split_probability; { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index,data->m_Feature.shape(1)); split_features = data->m_Feature.subarray(lowerBound,upperBound); } { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index, data->m_Label.shape(1)); split_labels = data->m_Label.subarray(lowerBound,upperBound); } { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index,data->m_Probabilities.shape(1)); split_probability = data->m_Probabilities.subarray(lowerBound,upperBound); } VigraPredictWeighted(data, split_features,split_labels,split_probability); return 0; } void mitk::VigraRandomForestClassifier::VigraPredictWeighted(PredictionData * data, vigra::MultiArrayView<2, double> & X, vigra::MultiArrayView<2, int> & Y, vigra::MultiArrayView<2, double> & P) { int isSampleWeighted = data->m_RandomForest.options_.predict_weighted_; //#pragma omp parallel for for(int row=0; row < vigra::rowCount(X); ++row) { vigra::MultiArrayView<2, double, vigra::StridedArrayTag> currentRow(rowVector(X, row)); vigra::ArrayVector::const_iterator weights; //totalWeight == totalVoteCount! double totalWeight = 0.0; //Let each tree classify... for(int k=0; km_RandomForest.options_.tree_count_; ++k) { //get weights predicted by single tree weights = data->m_RandomForest.trees_[k /*tree_indices_[k]*/].predict(currentRow); double numberOfLeafObservations = (*(weights-1)); //update votecount. for(int l=0; lm_RandomForest.ext_param_.class_count_; ++l) { // Either the original weights are taken or the tree is additional weighted by the number of Observations in the leaf node. double cur_w = weights[l] * (isSampleWeighted * numberOfLeafObservations + (1-isSampleWeighted)); cur_w = cur_w * data->m_TreeWeights(k,0); P(row, l) += (int)cur_w; //every weight in totalWeight. totalWeight += cur_w; } } //Normalise votes in each row by total VoteCount (totalWeight for(int l=0; l< data->m_RandomForest.ext_param_.class_count_; ++l) { P(row, l) /= vigra::detail::RequiresExplicitCast::cast(totalWeight); } int erg; int maxCol = 0; for (int col=0;colm_RandomForest.class_count();++col) { if (data->m_Probabilities(row,col) > data->m_Probabilities(row, maxCol)) maxCol = col; } data->m_RandomForest.ext_param_.to_classlabel(maxCol, erg); Y(row,0) = erg; } } void mitk::VigraRandomForestClassifier::ConvertParameter() { if(this->m_Parameter == nullptr) this->m_Parameter = new Parameter(); // Get the proerty // Some defaults MITK_INFO("VigraRandomForestClassifier") << "Convert Parameter"; if(!this->GetPropertyList()->Get("usepointbasedweight",this->m_Parameter->UsePointBasedWeights)) this->m_Parameter->UsePointBasedWeights = false; if(!this->GetPropertyList()->Get("userandomsplit",this->m_Parameter->UseRandomSplit)) this->m_Parameter->UseRandomSplit = false; if(!this->GetPropertyList()->Get("treedepth",this->m_Parameter->TreeDepth)) this->m_Parameter->TreeDepth = 20; if(!this->GetPropertyList()->Get("treecount",this->m_Parameter->TreeCount)) this->m_Parameter->TreeCount = 100; if(!this->GetPropertyList()->Get("minimalsplitnodesize",this->m_Parameter->MinimumSplitNodeSize)) this->m_Parameter->MinimumSplitNodeSize = 5; if(!this->GetPropertyList()->Get("precision",this->m_Parameter->Precision)) this->m_Parameter->Precision = mitk::eps; if(!this->GetPropertyList()->Get("samplespertree",this->m_Parameter->SamplesPerTree)) this->m_Parameter->SamplesPerTree = 0.6; if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->SampleWithReplacement)) this->m_Parameter->SampleWithReplacement = true; if(!this->GetPropertyList()->Get("lambda",this->m_Parameter->WeightLambda)) this->m_Parameter->WeightLambda = 1.0; // Not used yet // if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->Stratification)) this->m_Parameter->Stratification = vigra::RF_NONE; // no Property given } void mitk::VigraRandomForestClassifier::PrintParameter(std::ostream & str) { if(this->m_Parameter == nullptr) { MITK_WARN("VigraRandomForestClassifier") << "Parameters are not initialized. Please call ConvertParameter() first!"; return; } this->ConvertParameter(); // Get the proerty // Some defaults if(!this->GetPropertyList()->Get("usepointbasedweight",this->m_Parameter->UsePointBasedWeights)) str << "usepointbasedweight\tNOT SET (default " << this->m_Parameter->UsePointBasedWeights << ")" << "\n"; else str << "usepointbasedweight\t" << this->m_Parameter->UsePointBasedWeights << "\n"; if(!this->GetPropertyList()->Get("userandomsplit",this->m_Parameter->UseRandomSplit)) str << "userandomsplit\tNOT SET (default " << this->m_Parameter->UseRandomSplit << ")" << "\n"; else str << "userandomsplit\t" << this->m_Parameter->UseRandomSplit << "\n"; if(!this->GetPropertyList()->Get("treedepth",this->m_Parameter->TreeDepth)) str << "treedepth\t\tNOT SET (default " << this->m_Parameter->TreeDepth << ")" << "\n"; else str << "treedepth\t\t" << this->m_Parameter->TreeDepth << "\n"; if(!this->GetPropertyList()->Get("minimalsplitnodesize",this->m_Parameter->MinimumSplitNodeSize)) str << "minimalsplitnodesize\tNOT SET (default " << this->m_Parameter->MinimumSplitNodeSize << ")" << "\n"; else str << "minimalsplitnodesize\t" << this->m_Parameter->MinimumSplitNodeSize << "\n"; if(!this->GetPropertyList()->Get("precision",this->m_Parameter->Precision)) str << "precision\t\tNOT SET (default " << this->m_Parameter->Precision << ")" << "\n"; else str << "precision\t\t" << this->m_Parameter->Precision << "\n"; if(!this->GetPropertyList()->Get("samplespertree",this->m_Parameter->SamplesPerTree)) str << "samplespertree\tNOT SET (default " << this->m_Parameter->SamplesPerTree << ")" << "\n"; else str << "samplespertree\t" << this->m_Parameter->SamplesPerTree << "\n"; if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->SampleWithReplacement)) str << "samplewithreplacement\tNOT SET (default " << this->m_Parameter->SampleWithReplacement << ")" << "\n"; else str << "samplewithreplacement\t" << this->m_Parameter->SampleWithReplacement << "\n"; if(!this->GetPropertyList()->Get("treecount",this->m_Parameter->TreeCount)) str << "treecount\t\tNOT SET (default " << this->m_Parameter->TreeCount << ")" << "\n"; else str << "treecount\t\t" << this->m_Parameter->TreeCount << "\n"; if(!this->GetPropertyList()->Get("lambda",this->m_Parameter->WeightLambda)) str << "lambda\t\tNOT SET (default " << this->m_Parameter->WeightLambda << ")" << "\n"; else str << "lambda\t\t" << this->m_Parameter->WeightLambda << "\n"; // if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->Stratification)) // this->m_Parameter->Stratification = vigra:RF_NONE; // no Property given } void mitk::VigraRandomForestClassifier::UsePointWiseWeight(bool val) { mitk::AbstractClassifier::UsePointWiseWeight(val); this->GetPropertyList()->SetBoolProperty("usepointbasedweight",val); } void mitk::VigraRandomForestClassifier::SetMaximumTreeDepth(int val) { this->GetPropertyList()->SetIntProperty("treedepth",val); } void mitk::VigraRandomForestClassifier::SetMinimumSplitNodeSize(int val) { this->GetPropertyList()->SetIntProperty("minimalsplitnodesize",val); } void mitk::VigraRandomForestClassifier::SetPrecision(double val) { this->GetPropertyList()->SetDoubleProperty("precision",val); } void mitk::VigraRandomForestClassifier::SetSamplesPerTree(double val) { this->GetPropertyList()->SetDoubleProperty("samplespertree",val); } void mitk::VigraRandomForestClassifier::UseSampleWithReplacement(bool val) { this->GetPropertyList()->SetBoolProperty("samplewithreplacement",val); } void mitk::VigraRandomForestClassifier::SetTreeCount(int val) { this->GetPropertyList()->SetIntProperty("treecount",val); } void mitk::VigraRandomForestClassifier::SetWeightLambda(double val) { this->GetPropertyList()->SetDoubleProperty("lambda",val); } void mitk::VigraRandomForestClassifier::SetTreeWeight(int treeId, double weight) { m_TreeWeights(treeId,0) = weight; } void mitk::VigraRandomForestClassifier::SetRandomForest(const vigra::RandomForest & rf) { this->SetMaximumTreeDepth(rf.ext_param().max_tree_depth); this->SetMinimumSplitNodeSize(rf.options().min_split_node_size_); this->SetTreeCount(rf.options().tree_count_); this->SetSamplesPerTree(rf.options().training_set_proportion_); this->UseSampleWithReplacement(rf.options().sample_with_replacement_); this->m_RandomForest = rf; } const vigra::RandomForest & mitk::VigraRandomForestClassifier::GetRandomForest() const { return this->m_RandomForest; } diff --git a/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkAdditionalRFData.cpp b/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkAdditionalRFData.cpp new file mode 100644 index 0000000000..913b6e41d8 --- /dev/null +++ b/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkAdditionalRFData.cpp @@ -0,0 +1,6 @@ +#include + +void mitk::PURFData::NoFunction() +{ + return; +} \ No newline at end of file diff --git a/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkImpurityLoss.cpp b/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkImpurityLoss.cpp index 527a5523f3..add5f035cc 100644 --- a/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkImpurityLoss.cpp +++ b/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkImpurityLoss.cpp @@ -1,111 +1,112 @@ #ifndef mitkImpurityLoss_cpp #define mitkImpurityLoss_cpp #include template template mitk::ImpurityLoss::ImpurityLoss(TLabelContainer const &labels, - vigra::ProblemSpec const &ext) : + vigra::ProblemSpec const &ext, + AdditionalRFDataAbstract * /*data*/) : m_UsePointWeights(false), m_Labels(labels), m_Counts(ext.class_count_, 0.0), m_ClassWeights(ext.class_weights_), m_TotalCount(0.0) { } template void mitk::ImpurityLoss::Reset() { m_Counts.init(0); m_TotalCount = 0.0; } template template double mitk::ImpurityLoss::Increment(TDataIterator begin, TDataIterator end) { for (TDataIterator iter = begin; iter != end; ++iter) { double pointProbability = 1.0; if (m_UsePointWeights) { pointProbability = m_PointWeights(*iter,0); } m_Counts[m_Labels(*iter,0)] += pointProbability; m_TotalCount += pointProbability; } return m_LossFunction(m_Counts, m_ClassWeights, m_TotalCount); } template template double mitk::ImpurityLoss::Decrement(TDataIterator begin, TDataIterator end) { for (TDataIterator iter = begin; iter != end; ++iter) { double pointProbability = 1.0; if (m_UsePointWeights) { pointProbability = m_PointWeights(*iter,0); } m_Counts[m_Labels(*iter,0)] -= pointProbability; m_TotalCount -= pointProbability; } return m_LossFunction(m_Counts, m_ClassWeights, m_TotalCount); } template template double mitk::ImpurityLoss::Init(TArray initCounts) { Reset(); std::copy(initCounts.begin(), initCounts.end(), m_Counts.begin()); m_TotalCount = std::accumulate(m_Counts.begin(), m_Counts.end(), 0.0); return m_LossFunction(m_Counts, m_ClassWeights, m_TotalCount); } template vigra::ArrayVector const& mitk::ImpurityLoss::Response() { return m_Counts; } template void mitk::ImpurityLoss::UsePointWeights(bool useWeights) { m_UsePointWeights = useWeights; } template bool mitk::ImpurityLoss::IsUsingPointWeights() { return m_UsePointWeights; } template void mitk::ImpurityLoss::SetPointWeights(TWeightContainer weight) { m_PointWeights = weight; } template typename mitk::ImpurityLoss::WeightContainerType mitk::ImpurityLoss::GetPointWeights() { return m_PointWeights; } #endif // mitkImpurityLoss_cpp diff --git a/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkLinearSplitting.cpp b/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkLinearSplitting.cpp index baf35964d1..b3b3de2d14 100644 --- a/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkLinearSplitting.cpp +++ b/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkLinearSplitting.cpp @@ -1,168 +1,184 @@ #ifndef mitkLinearSplitting_cpp #define mitkLinearSplitting_cpp #include +#include template mitk::LinearSplitting::LinearSplitting() : m_UsePointWeights(false), - m_UseRandomSplit(false) + m_UseRandomSplit(false), + m_AdditionalData(nullptr) { } template template mitk::LinearSplitting::LinearSplitting(vigra::ProblemSpec const &ext) : m_UsePointWeights(false), m_UseRandomSplit(false) { set_external_parameters(ext); } +template +void +mitk::LinearSplitting::SetAdditionalData(AdditionalRFDataAbstract* data) +{ + m_AdditionalData = data; +} + +template +mitk::AdditionalRFDataAbstract * +mitk::LinearSplitting::GetAdditionalData() const +{ + return m_AdditionalData; +} + template void mitk::LinearSplitting::UsePointWeights(bool pointWeight) { m_UsePointWeights = pointWeight; } template bool mitk::LinearSplitting::IsUsingPointWeights() { return m_UsePointWeights; } template void mitk::LinearSplitting::UseRandomSplit(bool randomSplit) { m_UseRandomSplit = randomSplit; } template bool mitk::LinearSplitting::IsUsingRandomSplit() { return m_UseRandomSplit; } template void mitk::LinearSplitting::SetPointWeights(WeightContainerType weight) { m_PointWeights = weight; } template typename mitk::LinearSplitting::WeightContainerType mitk::LinearSplitting::GetPointWeights() { return m_PointWeights; } template template void mitk::LinearSplitting::set_external_parameters(vigra::ProblemSpec const &ext) { m_ExtParameter = ext; } template template void mitk::LinearSplitting::operator()(TDataSourceFeature const &column, TDataSourceLabel const &labels, TDataIterator &begin, TDataIterator &end, TArray const ®ionResponse) { typedef TLossAccumulator LineSearchLoss; std::sort(begin, end, vigra::SortSamplesByDimensions(column, 0)); - LineSearchLoss left(labels, m_ExtParameter); - LineSearchLoss right(labels, m_ExtParameter); + LineSearchLoss left(labels, m_ExtParameter, m_AdditionalData); + LineSearchLoss right(labels, m_ExtParameter, m_AdditionalData); if (m_UsePointWeights) { left.UsePointWeights(true); left.SetPointWeights(m_PointWeights); right.UsePointWeights(true); right.SetPointWeights(m_PointWeights); } m_MinimumLoss = right.Init(regionResponse); m_MinimumThreshold = *begin; m_MinimumIndex = 0; vigra::DimensionNotEqual compareNotEqual(column, 0); if (!m_UseRandomSplit) { TDataIterator iter = begin; // Find the next element that are NOT equal with his neightbour! TDataIterator next = std::adjacent_find(iter, end, compareNotEqual); while(next != end) { // Remove or add the current segment are from the LineSearch double rightLoss = right.Decrement(iter, next +1); double leftLoss = left.Increment(iter, next +1); double currentLoss = rightLoss + leftLoss; if (currentLoss < m_MinimumLoss) { m_BestCurrentCounts[0] = left.Response(); m_BestCurrentCounts[1] = right.Response(); m_MinimumLoss = currentLoss; m_MinimumIndex = next - begin + 1; m_MinimumThreshold = (double(column(*next,0)) + double(column(*(next +1), 0)))/2.0; } iter = next + 1; next = std::adjacent_find(iter, end, compareNotEqual); } } else // If Random split is selected, e.g. ExtraTree behaviour { int size = end - begin + 1; srand(time(nullptr)); int offset = rand() % size; TDataIterator iter = begin + offset; double rightLoss = right.Decrement(begin, iter+1); double leftLoss = left.Increment(begin, iter+1); double currentLoss = rightLoss + leftLoss; if (currentLoss < m_MinimumLoss) { m_BestCurrentCounts[0] = left.Response(); m_BestCurrentCounts[1] = right.Response(); m_MinimumLoss = currentLoss; m_MinimumIndex = offset + 1; m_MinimumThreshold = (double(column(*iter,0)) + double(column(*(iter+1), 0)))/2.0; } } } template template double mitk::LinearSplitting::LossOfRegion(TDataSourceLabel const & labels, TDataIterator &/*begin*/, TDataIterator &/*end*/, TArray const & regionResponse) { typedef TLossAccumulator LineSearchLoss; - LineSearchLoss regionLoss(labels, m_ExtParameter); + LineSearchLoss regionLoss(labels, m_ExtParameter, m_AdditionalData); if (m_UsePointWeights) { regionLoss.UsePointWeights(true); regionLoss.SetPointWeights(m_PointWeights); } return regionLoss.Init(regionResponse); } #endif //mitkLinearSplitting_cpp diff --git a/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkPUImpurityLoss.cpp b/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkPUImpurityLoss.cpp new file mode 100644 index 0000000000..3259b742b5 --- /dev/null +++ b/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkPUImpurityLoss.cpp @@ -0,0 +1,136 @@ +#ifndef mitkPUImpurityLoss_cpp +#define mitkPUImpurityLoss_cpp + +#include +#include + +template +template +mitk::PUImpurityLoss::PUImpurityLoss(TLabelContainer const &labels, + vigra::ProblemSpec const &ext, + AdditionalRFDataAbstract *data) : + m_UsePointWeights(false), + m_Labels(labels), + //m_Kappa(ext.kappa_), // Not possible due to data type + m_Counts(ext.class_count_, 0.0), + m_PUCounts(ext.class_count_, 0.0), + m_ClassWeights(ext.class_weights_), + m_TotalCount(0.0), + m_PUTotalCount(0.0), + m_ClassCount(ext.class_count_) +{ + mitk::PURFData * purfdata = dynamic_cast (data); + //const PURFProblemSpec *problem = static_cast * > (&ext); + m_Kappa = vigra::ArrayVector(purfdata->m_Kappa); +} + +template +void +mitk::PUImpurityLoss::Reset() +{ + m_Counts.init(0); + m_TotalCount = 0.0; +} + +template +void +mitk::PUImpurityLoss::UpdatePUCounts() +{ + m_PUTotalCount = 0; + for (int i = 1; i < m_ClassCount; ++i) + { + m_PUCounts[i] = m_Kappa[i] * m_Counts[i]; + m_PUTotalCount += m_PUCounts[i]; + } + m_PUCounts[0] = std::max(0.0, m_TotalCount - m_PUTotalCount); + m_PUTotalCount += m_PUCounts[0]; +} + +template +template +double +mitk::PUImpurityLoss::Increment(TDataIterator begin, TDataIterator end) +{ + for (TDataIterator iter = begin; iter != end; ++iter) + { + double pointProbability = 1.0; + if (m_UsePointWeights) + { + pointProbability = m_PointWeights(*iter,0); + } + m_Counts[m_Labels(*iter,0)] += pointProbability; + m_TotalCount += pointProbability; + } + UpdatePUCounts(); + return m_LossFunction(m_PUCounts, m_ClassWeights, m_PUTotalCount); +} + +template +template +double +mitk::PUImpurityLoss::Decrement(TDataIterator begin, TDataIterator end) +{ + for (TDataIterator iter = begin; iter != end; ++iter) + { + double pointProbability = 1.0; + if (m_UsePointWeights) + { + pointProbability = m_PointWeights(*iter,0); + } + m_Counts[m_Labels(*iter,0)] -= pointProbability; + m_TotalCount -= pointProbability; + } + UpdatePUCounts(); + return m_LossFunction(m_PUCounts, m_ClassWeights, m_PUTotalCount); +} + +template +template +double +mitk::PUImpurityLoss::Init(TArray initCounts) +{ + Reset(); + std::copy(initCounts.begin(), initCounts.end(), m_Counts.begin()); + m_TotalCount = std::accumulate(m_Counts.begin(), m_Counts.end(), 0.0); + return m_LossFunction(m_Counts, m_ClassWeights, m_TotalCount); +} + +template +vigra::ArrayVector const& +mitk::PUImpurityLoss::Response() +{ + return m_Counts; +} + +template +void +mitk::PUImpurityLoss::UsePointWeights(bool useWeights) +{ + m_UsePointWeights = useWeights; +} + +template +bool +mitk::PUImpurityLoss::IsUsingPointWeights() +{ + return m_UsePointWeights; +} + +template +void +mitk::PUImpurityLoss::SetPointWeights(TWeightContainer weight) +{ + m_PointWeights = weight; +} + +template +typename mitk::PUImpurityLoss::WeightContainerType +mitk::PUImpurityLoss::GetPointWeights() +{ + return m_PointWeights; +} + + +#endif // mitkImpurityLoss_cpp + + diff --git a/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkThresholdSplit.cpp b/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkThresholdSplit.cpp index 86a2f635a8..388b3c27cb 100644 --- a/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkThresholdSplit.cpp +++ b/Modules/Classification/CLVigraRandomForest/src/Splitter/mitkThresholdSplit.cpp @@ -1,298 +1,313 @@ #ifndef mitkThresholdSplit_cpp #define mitkThresholdSplit_cpp #include template mitk::ThresholdSplit::ThresholdSplit() : m_CalculatingFeature(false), m_UseWeights(false), m_UseRandomSplit(false), m_Precision(0.0), - m_MaximumTreeDepth(1000) + m_MaximumTreeDepth(1000), + m_AdditionalData(nullptr) { } //template //mitk::ThresholdSplit::ThresholdSplit(const ThresholdSplit & /*other*/)/*: // m_CalculatingFeature(other.IsCalculatingFeature()), // m_UseWeights(other.IsUsingPointBasedWeights()), // m_UseRandomSplit(other.IsUsingRandomSplit()), // m_Precision(other.GetPrecision()), // m_MaximumTreeDepth(other.GetMaximumTreeDepth()), // m_FeatureCalculator(other.GetFeatureCalculator()), // m_Weights(other.GetWeights())*/ //{ //} +template +void +mitk::ThresholdSplit::SetAdditionalData(AdditionalRFDataAbstract* data) +{ + bgfunc.SetAdditionalData(data); + m_AdditionalData = data; +} + +template +mitk::AdditionalRFDataAbstract * +mitk::ThresholdSplit::GetAdditionalData() const +{ + return m_AdditionalData; +} template void mitk::ThresholdSplit::SetFeatureCalculator(TFeatureCalculator processor) { m_FeatureCalculator = processor; } template TFeatureCalculator mitk::ThresholdSplit::GetFeatureCalculator() const { return m_FeatureCalculator; } template void mitk::ThresholdSplit::SetCalculatingFeature(bool calculate) { m_CalculatingFeature = calculate; } template bool mitk::ThresholdSplit::IsCalculatingFeature() const { return m_CalculatingFeature; } template void mitk::ThresholdSplit::UsePointBasedWeights(bool weightsOn) { m_UseWeights = weightsOn; bgfunc.UsePointWeights(weightsOn); } template bool mitk::ThresholdSplit::IsUsingPointBasedWeights() const { return m_UseWeights; } template void mitk::ThresholdSplit::SetPrecision(double value) { m_Precision = value; } template double mitk::ThresholdSplit::GetPrecision() const { return m_Precision; } template void mitk::ThresholdSplit::SetMaximumTreeDepth(int value) { m_MaximumTreeDepth = value; } template int mitk::ThresholdSplit::GetMaximumTreeDepth() const { return m_MaximumTreeDepth; } template void mitk::ThresholdSplit::SetWeights(vigra::MultiArrayView<2, double> weights) { m_Weights = weights; bgfunc.UsePointWeights(m_UseWeights); bgfunc.SetPointWeights(weights); } template vigra::MultiArrayView<2, double> mitk::ThresholdSplit::GetWeights() const { return m_Weights; } template double mitk::ThresholdSplit::minGini() const { return min_gini_[bestSplitIndex]; } template int mitk::ThresholdSplit::bestSplitColumn() const { return splitColumns[bestSplitIndex]; } template double mitk::ThresholdSplit::bestSplitThreshold() const { return min_thresholds_[bestSplitIndex]; } template template void mitk::ThresholdSplit::set_external_parameters(vigra::ProblemSpec const & in) { SB::set_external_parameters(in); bgfunc.set_external_parameters( SB::ext_param_); int featureCount_ = SB::ext_param_.column_count_; splitColumns.resize(featureCount_); for(int k=0; k template int mitk::ThresholdSplit::findBestSplit(vigra::MultiArrayView<2, T, C> features, vigra::MultiArrayView<2, T2, C2> labels, Region & region, vigra::ArrayVector& childRegions, Random & randint) { typedef typename Region::IndexIterator IndexIteratorType; if (m_CalculatingFeature) { // Do some very fance stuff here!! // This is not so simple as it might look! We need to // remember which feature has been used to be able to // use it for testing again!! // There, no Splitting class is used!! } bgfunc.UsePointWeights(m_UseWeights); bgfunc.UseRandomSplit(m_UseRandomSplit); vigra::detail::Correction::exec(region, labels); // Create initial class count. for(std::size_t i = 0; i < region.classCounts_.size(); ++i) { region.classCounts_[i] = 0; } double regionSum = 0; for (typename Region::IndexIterator iter = region.begin(); iter != region.end(); ++iter) { double probability = 1.0; if (m_UseWeights) { probability = m_Weights(*iter, 0); } region.classCounts_[labels(*iter,0)] += probability; regionSum += probability; } region.classCountsIsValid = true; vigra::ArrayVector vec; // Is pure region? region_gini_ = bgfunc.LossOfRegion(labels, region.begin(), region.end(), region.classCounts()); if (region_gini_ <= m_Precision * regionSum) // Necessary to fix wrong calculation of Gini-Index { return this->makeTerminalNode(features, labels, region, randint); } // Randomize the order of columns for (int i = 0; i < SB::ext_param_.actual_mtry_; ++i) { std::swap(splitColumns[i], splitColumns[i+ randint(features.shape(1) - i)]); } // find the split with the best evaluation value bestSplitIndex = 0; double currentMiniGini = region_gini_; int numberOfTrials = features.shape(1); for (int k = 0; k < numberOfTrials; ++k) { bgfunc(columnVector(features, splitColumns[k]), labels, region.begin(), region.end(), region.classCounts()); min_gini_[k] = bgfunc.GetMinimumLoss(); min_indices_[k] = bgfunc.GetMinimumIndex(); min_thresholds_[k] = bgfunc.GetMinimumThreshold(); // removed classifier test section, because not necessary if (bgfunc.GetMinimumLoss() < currentMiniGini) { currentMiniGini = bgfunc.GetMinimumLoss(); childRegions[0].classCounts() = bgfunc.GetBestCurrentCounts()[0]; childRegions[1].classCounts() = bgfunc.GetBestCurrentCounts()[1]; childRegions[0].classCountsIsValid = true; childRegions[1].classCountsIsValid = true; bestSplitIndex = k; numberOfTrials = SB::ext_param_.actual_mtry_; } } //If only a small improvement, make terminal node... if(vigra::closeAtTolerance(currentMiniGini, region_gini_)) { return this->makeTerminalNode(features, labels, region, randint); } vigra::Node node(SB::t_data, SB::p_data); SB::node_ = node; node.threshold() = min_thresholds_[bestSplitIndex]; node.column() = splitColumns[bestSplitIndex]; // partition the range according to the best dimension vigra::SortSamplesByDimensions > sorter(features, node.column(), node.threshold()); IndexIteratorType bestSplit = std::partition(region.begin(), region.end(), sorter); // Save the ranges of the child stack entries. childRegions[0].setRange( region.begin() , bestSplit ); childRegions[0].rule = region.rule; childRegions[0].rule.push_back(std::make_pair(1, 1.0)); childRegions[1].setRange( bestSplit , region.end() ); childRegions[1].rule = region.rule; childRegions[1].rule.push_back(std::make_pair(1, 1.0)); return vigra::i_ThresholdNode; return 0; } //template //static void UpdateRegionCounts(TRegion & region, TRegionIterator begin, TRegionIterator end, TLabelHolder labels, TWeightsHolder weights) //{ // if(std::accumulate(region.classCounts().begin(), // region.classCounts().end(), 0.0) != region.size()) // { // RandomForestClassCounter< LabelT, // ArrayVector > // counter(labels, region.classCounts()); // std::for_each( region.begin(), region.end(), counter); // region.classCountsIsValid = true; // } //} // //template //static void exec(Region & region, LabelT & labels) //{ // if(std::accumulate(region.classCounts().begin(), // region.classCounts().end(), 0.0) != region.size()) // { // RandomForestClassCounter< LabelT, // ArrayVector > // counter(labels, region.classCounts()); // std::for_each( region.begin(), region.end(), counter); // region.classCountsIsValid = true; // } //} #endif //mitkThresholdSplit_cpp diff --git a/Modules/Classification/DataCollection/Utilities/mitkCollectionStatistic.h b/Modules/Classification/DataCollection/Utilities/mitkCollectionStatistic.h index a47810c857..0d351070f8 100644 --- a/Modules/Classification/DataCollection/Utilities/mitkCollectionStatistic.h +++ b/Modules/Classification/DataCollection/Utilities/mitkCollectionStatistic.h @@ -1,137 +1,146 @@ #ifndef mitkCollectionStatistic_h #define mitkCollectionStatistic_h #include #include #include namespace mitk { struct MITKDATACOLLECTION_EXPORT StatisticData { unsigned int m_TruePositive; unsigned int m_FalsePositive; unsigned int m_TrueNegative; unsigned int m_FalseNegative; double m_DICE; double m_Jaccard; double m_Sensitivity; double m_Specificity; double m_RMSD; StatisticData() : m_TruePositive(0), m_FalsePositive(0), m_TrueNegative(0), m_FalseNegative(0), m_DICE(0), m_Jaccard(0), m_Sensitivity(0), m_Specificity(0), m_RMSD(-1.0) {} }; class ValueToIndexMapper { public: virtual unsigned char operator() (unsigned char value) const = 0; }; +class BinaryValueminusOneToIndexMapper : public virtual ValueToIndexMapper +{ +public: + unsigned char operator() (unsigned char value) const + { + return value-1; + } +}; + class BinaryValueToIndexMapper : public virtual ValueToIndexMapper { public: unsigned char operator() (unsigned char value) const override { return value; } }; class MultiClassValueToIndexMapper : public virtual ValueToIndexMapper { public: unsigned char operator() (unsigned char value) const override { if (value == 1 || value == 5) return 0; else return 1; } }; class ProgressionValueToIndexMapper : public virtual ValueToIndexMapper { public: unsigned char operator() (unsigned char value) const override { if (value == 1 || value == 0) return 0; else return 1; } }; class MITKDATACOLLECTION_EXPORT CollectionStatistic { public: CollectionStatistic(); ~CollectionStatistic(); typedef std::vector DataVector; typedef std::vector MultiDataVector; void SetCollection(DataCollection::Pointer collection); DataCollection::Pointer GetCollection(); void SetClassCount (size_t count); size_t GetClassCount(); void SetGoldName(std::string name); std::string GetGoldName(); void SetTestName(std::string name); std::string GetTestName(); void SetMaskName(std::string name) {m_MaskName = name; } void SetGroundTruthValueToIndexMapper(const ValueToIndexMapper* mapper); const ValueToIndexMapper* GetGroundTruthValueToIndexMapper(void) const; void SetTestValueToIndexMapper(const ValueToIndexMapper* mapper); const ValueToIndexMapper* GetTestValueToIndexMapper(void) const; void Print(std::ostream& out, std::ostream& sout = std::cout, bool withHeader = false, std::string label = "None"); bool Update(); int IsInSameVirtualClass(unsigned char gold, unsigned char test); /** * @brief mitk::CollectionStatistic::GetStatisticData * @param c The class for which to retrieve the statistic data. * @return */ std::vector GetStatisticData(unsigned char c) const; /** * @brief Computes root-mean-square distance of two binary images. */ void ComputeRMSD(); private: size_t m_ClassCount; std::string m_GroundTruthName; std::string m_TestName; std::string m_MaskName; DataCollection::Pointer m_Collection; std::vector m_ConnectionGold; std::vector m_ConnectionTest; std::vector m_ConnectionClass; MultiDataVector m_ImageClassStatistic; std::vector m_ImageNames; DataVector m_ImageStatistic; StatisticData m_MeanCompleteStatistic; StatisticData m_CompleteStatistic; const ValueToIndexMapper* m_GroundTruthValueToIndexMapper; const ValueToIndexMapper* m_TestValueToIndexMapper; }; } #endif // mitkCollectionStatistic_h diff --git a/Modules/Classification/Scripts/xvfb-run-save.sh b/Modules/Classification/Scripts/xvfb-run-save.sh new file mode 100644 index 0000000000..d5a26df183 --- /dev/null +++ b/Modules/Classification/Scripts/xvfb-run-save.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# allow settings to be updated via environment +: "${xvfb_lockdir:=$HOME/.xvfb-locks}" +: "${xvfb_display_min:=99}" +: "${xvfb_display_max:=599}" + +# assuming only one user will use this, let's put the locks in our own home directory +# avoids vulnerability to symlink attacks. +mkdir -p -- "$xvfb_lockdir" || exit + +i=$xvfb_display_min # minimum display number +while (( i < xvfb_display_max )); do + if [ -f "/tmp/.X$i-lock" ]; then # still avoid an obvious open display + (( ++i )); continue + fi + exec 5>"$xvfb_lockdir/$i" || continue # open a lockfile + if flock -x -n 5; then # try to lock it +# echo xvfb-run --server-num="$i" --server-args="'-screen 0 1024x768x24'" "$@" + exec xvfb-run -e ~/xvfblog.log --server-num="$i" --server-args='-screen 0 1024x768x24' "$@" || exit # if locked, run xvfb-run + fi + (( i++ )) +done + + + + + + + + + + + + + + + + + + diff --git a/Modules/DiffusionImaging/MiniApps/ExtractAllGradients.cpp b/Modules/DiffusionImaging/MiniApps/ExtractAllGradients.cpp new file mode 100644 index 0000000000..b878cb2075 --- /dev/null +++ b/Modules/DiffusionImaging/MiniApps/ExtractAllGradients.cpp @@ -0,0 +1,86 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include +#include +#include "mitkCommandLineParser.h" +#include + +using namespace mitk; +using namespace std; + +/*! +\brief Copies transformation matrix of one image to another +*/ +int main(int argc, char* argv[]) +{ + typedef itk::VectorImage< short, 3 > ItkDwiType; + + mitkCommandLineParser parser; + + parser.setTitle("Extract all gradients"); + parser.setCategory("Preprocessing Tools"); + parser.setDescription("Extract all gradients from an diffusion image"); + parser.setContributor("MBI"); + + parser.setArgumentPrefix("--", "-"); + parser.addArgument("in", "i", mitkCommandLineParser::InputFile, "Input:", "input image", us::Any(), false); + //parser.addArgument("extension", "e", mitkCommandLineParser::String, "File Extension:", "Extension of the output file", us::Any(), false); + //parser.addArgument("out", "o", mitkCommandLineParser::OutputFile, "Output:", "output image", us::Any(), false); + + map parsedArgs = parser.parseArguments(argc, argv); + if (parsedArgs.size() == 0) + return EXIT_FAILURE; + + MITK_INFO << "Extract parameter"; + // mandatory arguments + /*string inputName = us::any_cast(parsedArgs["in"]); + string extensionName = us::any_cast(parsedArgs["extension"]); + string ouputName = us::any_cast(parsedArgs["out"]);*/ + string in = us::any_cast(parsedArgs["in"]); + string inputName = "E:\\Kollektive\\R02-Lebertumore-Diffusion\\01-Extrahierte-Daten\\" + in + "\\" + in + "-DWI.dwi"; + string extensionName = ".nrrd"; + string ouputName = "E:\\Kollektive\\R02-Lebertumore-Diffusion\\01-Extrahierte-Daten\\" + in + "\\" + in + "-"; + + MITK_INFO << "Load Image: "; + mitk::Image::Pointer image = mitk::IOUtil::LoadImage(inputName); + + //bool isDiffusionImage(mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(image)); + //if (!isDiffusionImage) + //{ + // MITK_INFO << "Input file is not of type diffusion image"; + // return; + //} + + ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); + mitk::CastToItkImage(image, itkVectorImagePointer); + + unsigned int channel = 0; + for (unsigned int channel = 0; channel < itkVectorImagePointer->GetVectorLength(); ++channel) + { + itk::ExtractDwiChannelFilter< short >::Pointer filter = itk::ExtractDwiChannelFilter< short >::New(); + filter->SetInput(itkVectorImagePointer); + filter->SetChannelIndex(channel); + filter->Update(); + + mitk::Image::Pointer newImage = mitk::Image::New(); + newImage->InitializeByItk(filter->GetOutput()); + newImage->SetImportChannel(filter->GetOutput()->GetBufferPointer()); + mitk::IOUtil::SaveImage(newImage, ouputName + to_string(channel) + extensionName); + } + return EXIT_SUCCESS; +} diff --git a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp index 257bf37e91..a2cc97da19 100644 --- a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp +++ b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp @@ -1,529 +1,530 @@ #include #include #include #include "mitkImageAccessByItk.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { void PlanarFigureMaskGenerator::SetPlanarFigure(mitk::PlanarFigure::Pointer planarFigure) { if ( planarFigure.IsNull() ) { throw std::runtime_error( "Error: planar figure empty!" ); } const PlaneGeometry *planarFigurePlaneGeometry = planarFigure->GetPlaneGeometry(); if ( planarFigurePlaneGeometry == nullptr ) { throw std::runtime_error( "Planar-Figure not yet initialized!" ); } const auto *planarFigureGeometry = dynamic_cast< const PlaneGeometry * >( planarFigurePlaneGeometry ); if ( planarFigureGeometry == nullptr ) { throw std::runtime_error( "Non-planar planar figures not supported!" ); } if (planarFigure != m_PlanarFigure) { this->Modified(); m_PlanarFigure = planarFigure; } } mitk::Image::Pointer PlanarFigureMaskGenerator::GetReferenceImage() { if (IsUpdateRequired()) { this->CalculateMask(); } return m_ReferenceImage; } template < typename TPixel, unsigned int VImageDimension > void PlanarFigureMaskGenerator::InternalCalculateMaskFromPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ) { typedef itk::Image< unsigned short, 2 > MaskImage2DType; typename MaskImage2DType::Pointer maskImage = MaskImage2DType::New(); maskImage->SetOrigin(image->GetOrigin()); maskImage->SetSpacing(image->GetSpacing()); maskImage->SetLargestPossibleRegion(image->GetLargestPossibleRegion()); maskImage->SetBufferedRegion(image->GetBufferedRegion()); maskImage->SetDirection(image->GetDirection()); maskImage->SetNumberOfComponentsPerPixel(image->GetNumberOfComponentsPerPixel()); maskImage->Allocate(); maskImage->FillBuffer(1); // all PolylinePoints of the PlanarFigure are stored in a vtkPoints object. // These points are used by the vtkLassoStencilSource to create // a vtkImageStencil. const mitk::PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); const typename PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); const mitk::BaseGeometry *imageGeometry3D = m_inputImage->GetGeometry( 0 ); // If there is a second poly line in a closed planar figure, treat it as a hole. PlanarFigure::PolyLineType planarFigureHolePolyline; if (m_PlanarFigure->GetPolyLinesSize() == 2) planarFigureHolePolyline = m_PlanarFigure->GetPolyLine(1); // Determine x- and y-dimensions depending on principal axis // TODO use plane geometry normal to determine that automatically, then check whether the PF is aligned with one of the three principal axis int i0, i1; switch ( axis ) { case 0: i0 = 1; i1 = 2; break; case 1: i0 = 0; i1 = 2; break; case 2: default: i0 = 0; i1 = 1; break; } // store the polyline contour as vtkPoints object bool outOfBounds = false; vtkSmartPointer points = vtkSmartPointer::New(); typename PlanarFigure::PolyLineType::const_iterator it; for ( it = planarFigurePolyline.begin(); it != planarFigurePolyline.end(); ++it ) { Point3D point3D; // Convert 2D point back to the local index coordinates of the selected // image // Fabian: From PlaneGeometry documentation: // Converts a 2D point given in mm (pt2d_mm) relative to the upper-left corner of the geometry into the corresponding world-coordinate (a 3D point in mm, pt3d_mm). // To convert a 2D point given in units (e.g., pixels in case of an image) into a 2D point given in mm (as required by this method), use IndexToWorld. planarFigurePlaneGeometry->Map( *it, point3D ); // Polygons (partially) outside of the image bounds can not be processed // further due to a bug in vtkPolyDataToImageStencil if ( !imageGeometry3D->IsInside( point3D ) ) { outOfBounds = true; } imageGeometry3D->WorldToIndex( point3D, point3D ); points->InsertNextPoint( point3D[i0], point3D[i1], 0 ); } vtkSmartPointer holePoints = nullptr; if (!planarFigureHolePolyline.empty()) { holePoints = vtkSmartPointer::New(); Point3D point3D; PlanarFigure::PolyLineType::const_iterator end = planarFigureHolePolyline.end(); for (it = planarFigureHolePolyline.begin(); it != end; ++it) { // Fabian: same as above planarFigurePlaneGeometry->Map(*it, point3D); imageGeometry3D->WorldToIndex(point3D, point3D); holePoints->InsertNextPoint(point3D[i0], point3D[i1], 0); } } // mark a malformed 2D planar figure ( i.e. area = 0 ) as out of bounds // this can happen when all control points of a rectangle lie on the same line = two of the three extents are zero double bounds[6] = {0, 0, 0, 0, 0, 0}; points->GetBounds( bounds ); bool extent_x = (fabs(bounds[0] - bounds[1])) < mitk::eps; bool extent_y = (fabs(bounds[2] - bounds[3])) < mitk::eps; bool extent_z = (fabs(bounds[4] - bounds[5])) < mitk::eps; // throw an exception if a closed planar figure is deformed, i.e. has only one non-zero extent if ( m_PlanarFigure->IsClosed() && ((extent_x && extent_y) || (extent_x && extent_z) || (extent_y && extent_z))) { mitkThrow() << "Figure has a zero area and cannot be used for masking."; } if ( outOfBounds ) { throw std::runtime_error( "Figure at least partially outside of image bounds!" ); } // create a vtkLassoStencilSource and set the points of the Polygon vtkSmartPointer lassoStencil = vtkSmartPointer::New(); lassoStencil->SetShapeToPolygon(); lassoStencil->SetPoints( points ); vtkSmartPointer holeLassoStencil = nullptr; if (holePoints.GetPointer() != nullptr) { holeLassoStencil = vtkSmartPointer::New(); holeLassoStencil->SetShapeToPolygon(); holeLassoStencil->SetPoints(holePoints); } // Export from ITK to VTK (to use a VTK filter) typedef itk::VTKImageImport< MaskImage2DType > ImageImportType; typedef itk::VTKImageExport< MaskImage2DType > ImageExportType; typename ImageExportType::Pointer itkExporter = ImageExportType::New(); itkExporter->SetInput( maskImage ); // itkExporter->SetInput( castFilter->GetOutput() ); vtkSmartPointer vtkImporter = vtkSmartPointer::New(); this->ConnectPipelines( itkExporter, vtkImporter ); // Apply the generated image stencil to the input image vtkSmartPointer imageStencilFilter = vtkSmartPointer::New(); imageStencilFilter->SetInputConnection( vtkImporter->GetOutputPort() ); imageStencilFilter->SetStencilConnection(lassoStencil->GetOutputPort()); imageStencilFilter->ReverseStencilOff(); imageStencilFilter->SetBackgroundValue( 0 ); imageStencilFilter->Update(); vtkSmartPointer holeStencilFilter = nullptr; if (holeLassoStencil.GetPointer() != nullptr) { holeStencilFilter = vtkSmartPointer::New(); holeStencilFilter->SetInputConnection(imageStencilFilter->GetOutputPort()); holeStencilFilter->SetStencilConnection(holeLassoStencil->GetOutputPort()); holeStencilFilter->ReverseStencilOn(); holeStencilFilter->SetBackgroundValue(0); holeStencilFilter->Update(); } // Export from VTK back to ITK vtkSmartPointer vtkExporter = vtkSmartPointer::New(); vtkExporter->SetInputConnection( holeStencilFilter.GetPointer() == nullptr ? imageStencilFilter->GetOutputPort() : holeStencilFilter->GetOutputPort()); vtkExporter->Update(); typename ImageImportType::Pointer itkImporter = ImageImportType::New(); this->ConnectPipelines( vtkExporter, itkImporter ); itkImporter->Update(); typedef itk::ImageDuplicator< ImageImportType::OutputImageType > DuplicatorType; DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( itkImporter->GetOutput() ); duplicator->Update(); // Store mask m_InternalITKImageMask2D = duplicator->GetOutput(); } template < typename TPixel, unsigned int VImageDimension > void PlanarFigureMaskGenerator::InternalCalculateMaskFromOpenPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ) { typedef itk::Image< unsigned short, 2 > MaskImage2DType; typedef itk::LineIterator< MaskImage2DType > LineIteratorType; typedef MaskImage2DType::IndexType IndexType2D; typedef std::vector< IndexType2D > IndexVecType; typename MaskImage2DType::Pointer maskImage = MaskImage2DType::New(); maskImage->SetOrigin(image->GetOrigin()); maskImage->SetSpacing(image->GetSpacing()); maskImage->SetLargestPossibleRegion(image->GetLargestPossibleRegion()); maskImage->SetBufferedRegion(image->GetBufferedRegion()); maskImage->SetDirection(image->GetDirection()); maskImage->SetNumberOfComponentsPerPixel(image->GetNumberOfComponentsPerPixel()); maskImage->Allocate(); maskImage->FillBuffer(0); // all PolylinePoints of the PlanarFigure are stored in a vtkPoints object. const mitk::PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); const typename PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); const mitk::BaseGeometry *imageGeometry3D = m_inputImage->GetGeometry( 0 ); // Determine x- and y-dimensions depending on principal axis // TODO use plane geometry normal to determine that automatically, then check whether the PF is aligned with one of the three principal axis int i0, i1; switch ( axis ) { case 0: i0 = 1; i1 = 2; break; case 1: i0 = 0; i1 = 2; break; case 2: default: i0 = 0; i1 = 1; break; } int numPolyLines = m_PlanarFigure->GetPolyLinesSize(); for ( int lineId = 0; lineId < numPolyLines; ++lineId ) { // store the polyline contour as vtkPoints object bool outOfBounds = false; IndexVecType pointIndices; typename PlanarFigure::PolyLineType::const_iterator it; for ( it = planarFigurePolyline.begin(); it != planarFigurePolyline.end(); ++it ) { Point3D point3D; planarFigurePlaneGeometry->Map( *it, point3D ); if ( !imageGeometry3D->IsInside( point3D ) ) { outOfBounds = true; } imageGeometry3D->WorldToIndex( point3D, point3D ); IndexType2D index2D; index2D[0] = point3D[i0]; index2D[1] = point3D[i1]; pointIndices.push_back( index2D ); } if ( outOfBounds ) { throw std::runtime_error( "Figure at least partially outside of image bounds!" ); } for ( IndexVecType::const_iterator it = pointIndices.begin(); it != pointIndices.end()-1; ++it ) { IndexType2D ind1 = *it; IndexType2D ind2 = *(it+1); LineIteratorType lineIt( maskImage, ind1, ind2 ); while ( !lineIt.IsAtEnd() ) { lineIt.Set( 1 ); ++lineIt; } } } // Store mask m_InternalITKImageMask2D = maskImage; } bool PlanarFigureMaskGenerator::GetPrincipalAxis( const BaseGeometry *geometry, Vector3D vector, unsigned int &axis ) { vector.Normalize(); for ( unsigned int i = 0; i < 3; ++i ) { Vector3D axisVector = geometry->GetAxisVector( i ); axisVector.Normalize(); if ( fabs( fabs( axisVector * vector ) - 1.0) < mitk::eps ) { axis = i; return true; } } return false; } void PlanarFigureMaskGenerator::CalculateMask() { if (m_inputImage.IsNull()) { MITK_ERROR << "Image is not set."; } if (m_PlanarFigure.IsNull()) { MITK_ERROR << "PlanarFigure is not set."; } if (m_TimeStep != 0) { MITK_WARN << "Multiple TimeSteps are not supported in PlanarFigureMaskGenerator (yet)."; } const BaseGeometry *imageGeometry = m_inputImage->GetGeometry(); if ( imageGeometry == nullptr ) { throw std::runtime_error( "Image geometry invalid!" ); } if (m_inputImage->GetTimeSteps() > 0) { mitk::ImageTimeSelector::Pointer imgTimeSel = mitk::ImageTimeSelector::New(); imgTimeSel->SetInput(m_inputImage); imgTimeSel->SetTimeNr(m_TimeStep); imgTimeSel->UpdateLargestPossibleRegion(); m_InternalTimeSliceImage = imgTimeSel->GetOutput(); } else { m_InternalTimeSliceImage = m_inputImage; } m_InternalITKImageMask2D = nullptr; const PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); const auto *planarFigureGeometry = dynamic_cast< const PlaneGeometry * >( planarFigurePlaneGeometry ); //const BaseGeometry *imageGeometry = m_inputImage->GetGeometry(); // Find principal direction of PlanarFigure in input image unsigned int axis; if ( !this->GetPrincipalAxis( imageGeometry, planarFigureGeometry->GetNormal(), axis ) ) { throw std::runtime_error( "Non-aligned planar figures not supported!" ); } m_PlanarFigureAxis = axis; // Find slice number corresponding to PlanarFigure in input image itk::Image< unsigned short, 3 >::IndexType index; imageGeometry->WorldToIndex( planarFigureGeometry->GetOrigin(), index ); unsigned int slice = index[axis]; + m_PlanarFigureSlice = slice; // extract image slice which corresponds to the planarFigure and store it in m_InternalImageSlice mitk::Image::Pointer inputImageSlice = extract2DImageSlice(axis, slice); //mitk::IOUtil::Save(inputImageSlice, "/home/fabian/inputSliceImage.nrrd"); // Compute mask from PlanarFigure // rastering for open planar figure: if ( !m_PlanarFigure->IsClosed() ) { AccessFixedDimensionByItk_1(inputImageSlice, InternalCalculateMaskFromOpenPlanarFigure, 2, axis) } else//for closed planar figure { AccessFixedDimensionByItk_1(inputImageSlice, InternalCalculateMaskFromPlanarFigure, 2, axis) } //convert itk mask to mitk::Image::Pointer and return it mitk::Image::Pointer planarFigureMaskImage; planarFigureMaskImage = mitk::GrabItkImageMemory(m_InternalITKImageMask2D); //mitk::IOUtil::Save(planarFigureMaskImage, "/home/fabian/planarFigureMaskImage.nrrd"); //Convert2Dto3DImageFilter::Pointer sliceTo3DImageConverter = Convert2Dto3DImageFilter::New(); //sliceTo3DImageConverter->SetInput(planarFigureMaskImage); //sliceTo3DImageConverter->Update(); //mitk::IOUtil::Save(sliceTo3DImageConverter->GetOutput(), "/home/fabian/3DsliceImage.nrrd"); m_ReferenceImage = inputImageSlice; //mitk::IOUtil::Save(m_ReferenceImage, "/home/fabian/referenceImage.nrrd"); m_InternalMask = planarFigureMaskImage; } void PlanarFigureMaskGenerator::SetTimeStep(unsigned int timeStep) { if (timeStep != m_TimeStep) { m_TimeStep = timeStep; } } mitk::Image::Pointer PlanarFigureMaskGenerator::GetMask() { if (IsUpdateRequired()) { this->CalculateMask(); this->Modified(); } m_InternalMaskUpdateTime = this->GetMTime(); return m_InternalMask; } mitk::Image::Pointer PlanarFigureMaskGenerator::extract2DImageSlice(unsigned int axis, unsigned int slice) { // Extract slice with given position and direction from image unsigned int dimension = m_InternalTimeSliceImage->GetDimension(); mitk::Image::Pointer imageSlice = mitk::Image::New(); if (dimension == 3) { ExtractImageFilter::Pointer imageExtractor = ExtractImageFilter::New(); imageExtractor->SetInput( m_InternalTimeSliceImage ); imageExtractor->SetSliceDimension( axis ); imageExtractor->SetSliceIndex( slice ); imageExtractor->Update(); imageSlice = imageExtractor->GetOutput(); } else if(dimension == 2) { imageSlice = m_InternalTimeSliceImage; } else { MITK_ERROR << "Unsupported image dimension. Dimension is: " << dimension << ". Only 2D and 3D images are supported."; } return imageSlice; } bool PlanarFigureMaskGenerator::IsUpdateRequired() const { unsigned long thisClassTimeStamp = this->GetMTime(); unsigned long internalMaskTimeStamp = m_InternalMask->GetMTime(); unsigned long planarFigureTimeStamp = m_PlanarFigure->GetMTime(); unsigned long inputImageTimeStamp = m_inputImage->GetMTime(); if (thisClassTimeStamp > m_InternalMaskUpdateTime) // inputs have changed { return true; } if (m_InternalMaskUpdateTime < planarFigureTimeStamp || m_InternalMaskUpdateTime < inputImageTimeStamp) // mask image has changed outside of this class { return true; } if (internalMaskTimeStamp > m_InternalMaskUpdateTime) // internal mask has been changed outside of this class { return true; } return false; } } diff --git a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h index dcc30b9e70..36911bfc06 100644 --- a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h +++ b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h @@ -1,132 +1,135 @@ #ifndef MITKPLANARFIGUREMASKGENERATOR #define MITKPLANARFIGUREMASKGENERATOR #include #include #include #include #include #include #include #include #include #include namespace mitk { /** * \class PlanarFigureMaskGenerator * \brief Derived from MaskGenerator. This class is used to convert a mitk::PlanarFigure into a binary image mask */ class MITKIMAGESTATISTICS_EXPORT PlanarFigureMaskGenerator: public MaskGenerator { public: /** Standard Self typedef */ typedef PlanarFigureMaskGenerator Self; typedef MaskGenerator Superclass; typedef itk::SmartPointer< Self > Pointer; typedef itk::SmartPointer< const Self > ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Runtime information support. */ itkTypeMacro(PlanarFigureMaskGenerator, MaskGenerator) /** * @brief GetMask Computes and returns the mask * @return mitk::Image::Pointer of the generated mask */ mitk::Image::Pointer GetMask() override; void SetPlanarFigure(mitk::PlanarFigure::Pointer planarFigure); mitk::Image::Pointer GetReferenceImage() override; /** * @brief SetTimeStep is used to set the time step for which the mask is to be generated * @param timeStep */ void SetTimeStep(unsigned int timeStep) override; + itkGetConstMacro(PlanarFigureAxis, unsigned int); + itkGetConstMacro(PlanarFigureSlice, unsigned int); protected: PlanarFigureMaskGenerator():Superclass(){ m_InternalMaskUpdateTime = 0; m_InternalMask = mitk::Image::New(); m_ReferenceImage = nullptr; m_PlanarFigureAxis = 0; } private: void CalculateMask(); template < typename TPixel, unsigned int VImageDimension > void InternalCalculateMaskFromPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ); template < typename TPixel, unsigned int VImageDimension > void InternalCalculateMaskFromOpenPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ); mitk::Image::Pointer extract2DImageSlice(unsigned int axis, unsigned int slice); bool GetPrincipalAxis(const BaseGeometry *geometry, Vector3D vector, unsigned int &axis ); /** Connection from ITK to VTK */ template void ConnectPipelines(ITK_Exporter exporter, vtkSmartPointer importer) { importer->SetUpdateInformationCallback(exporter->GetUpdateInformationCallback()); importer->SetPipelineModifiedCallback(exporter->GetPipelineModifiedCallback()); importer->SetWholeExtentCallback(exporter->GetWholeExtentCallback()); importer->SetSpacingCallback(exporter->GetSpacingCallback()); importer->SetOriginCallback(exporter->GetOriginCallback()); importer->SetScalarTypeCallback(exporter->GetScalarTypeCallback()); importer->SetNumberOfComponentsCallback(exporter->GetNumberOfComponentsCallback()); importer->SetPropagateUpdateExtentCallback(exporter->GetPropagateUpdateExtentCallback()); importer->SetUpdateDataCallback(exporter->GetUpdateDataCallback()); importer->SetDataExtentCallback(exporter->GetDataExtentCallback()); importer->SetBufferPointerCallback(exporter->GetBufferPointerCallback()); importer->SetCallbackUserData(exporter->GetCallbackUserData()); } /** Connection from VTK to ITK */ template void ConnectPipelines(vtkSmartPointer exporter, ITK_Importer importer) { importer->SetUpdateInformationCallback(exporter->GetUpdateInformationCallback()); importer->SetPipelineModifiedCallback(exporter->GetPipelineModifiedCallback()); importer->SetWholeExtentCallback(exporter->GetWholeExtentCallback()); importer->SetSpacingCallback(exporter->GetSpacingCallback()); importer->SetOriginCallback(exporter->GetOriginCallback()); importer->SetScalarTypeCallback(exporter->GetScalarTypeCallback()); importer->SetNumberOfComponentsCallback(exporter->GetNumberOfComponentsCallback()); importer->SetPropagateUpdateExtentCallback(exporter->GetPropagateUpdateExtentCallback()); importer->SetUpdateDataCallback(exporter->GetUpdateDataCallback()); importer->SetDataExtentCallback(exporter->GetDataExtentCallback()); importer->SetBufferPointerCallback(exporter->GetBufferPointerCallback()); importer->SetCallbackUserData(exporter->GetCallbackUserData()); } bool IsUpdateRequired() const; mitk::PlanarFigure::Pointer m_PlanarFigure; itk::Image::Pointer m_InternalITKImageMask2D; mitk::Image::Pointer m_InternalTimeSliceImage; mitk::Image::Pointer m_ReferenceImage; unsigned int m_PlanarFigureAxis; unsigned long m_InternalMaskUpdateTime; + unsigned int m_PlanarFigureSlice; }; } #endif // MITKPLANARFIGUREMASKGENERATOR diff --git a/Modules/ModuleList.cmake b/Modules/ModuleList.cmake index 220afef5f0..8ac0be92e5 100644 --- a/Modules/ModuleList.cmake +++ b/Modules/ModuleList.cmake @@ -1,77 +1,77 @@ # The entries in the mitk_modules list must be # ordered according to their dependencies. set(MITK_MODULES Core CommandLine AppUtil RDF LegacyIO DataTypesExt Annotation LegacyGL AlgorithmsExt MapperExt DICOMReader DICOMReaderServices DICOMTesting SceneSerializationBase PlanarFigure ImageDenoising ImageExtraction SceneSerialization Gizmo GraphAlgorithms Multilabel ImageStatistics ContourModel SurfaceInterpolation Segmentation PlanarFigureSegmentation QtWidgets QtWidgetsExt Chart SegmentationUI + MatchPointRegistration + MatchPointRegistrationUI Classification GPGPU OpenIGTLink IGTBase IGT CameraCalibration OpenCL OpenCVVideoSupport QtOverlays ToFHardware ToFProcessing ToFUI PhotoacousticsHardware PhotoacousticsAlgorithms PhotoacousticsLib US USUI DicomUI Remeshing Python QtPython Persistence OpenIGTLinkUI IGTUI DicomRT RTUI IOExt XNAT TubeGraph BiophotonicsHardware - MatchPointRegistration - MatchPointRegistrationUI DiffusionImaging TumorInvasionAnalysis BoundingShape RenderWindowManager RenderWindowManagerUI CEST ) if(MITK_ENABLE_PIC_READER) list(APPEND MITK_MODULES IpPicSupportIO) endif() diff --git a/Plugins/PluginList.cmake b/Plugins/PluginList.cmake index 9ccec01655..03f3de6e54 100644 --- a/Plugins/PluginList.cmake +++ b/Plugins/PluginList.cmake @@ -1,101 +1,102 @@ # Plug-ins must be ordered according to their dependencies set(MITK_PLUGINS org.blueberry.core.runtime:ON org.blueberry.core.expressions:OFF org.blueberry.core.commands:OFF org.blueberry.core.jobs:OFF org.blueberry.ui.qt:OFF org.blueberry.ui.qt.help:ON org.blueberry.ui.qt.log:ON org.blueberry.ui.qt.objectinspector:OFF #org.blueberry.test:ON #org.blueberry.uitest:ON #Testing/org.blueberry.core.runtime.tests:ON #Testing/org.blueberry.osgi.tests:ON org.mitk.core.services:ON org.mitk.gui.common:ON org.mitk.planarfigure:ON org.mitk.core.ext:OFF org.mitk.core.jobs:OFF org.mitk.gui.qt.application:ON org.mitk.gui.qt.coreapplication:OFF org.mitk.gui.qt.ext:OFF org.mitk.gui.qt.extapplication:OFF org.mitk.gui.qt.common:ON org.mitk.gui.qt.stdmultiwidgeteditor:ON org.mitk.gui.qt.common.legacy:OFF org.mitk.gui.qt.cmdlinemodules:OFF org.mitk.gui.qt.diffusionimagingapp:OFF org.mitk.gui.qt.datamanager:ON org.mitk.gui.qt.datamanagerlight:OFF org.mitk.gui.qt.properties:ON org.mitk.gui.qt.basicimageprocessing:OFF org.mitk.gui.qt.dicom:OFF org.mitk.gui.qt.dicominspector:OFF org.mitk.gui.qt.diffusionimaging:OFF org.mitk.gui.qt.diffusionimaging.connectomics:OFF org.mitk.gui.qt.diffusionimaging.denoising:OFF org.mitk.gui.qt.diffusionimaging.fiberfox:OFF org.mitk.gui.qt.diffusionimaging.fiberprocessing:OFF org.mitk.gui.qt.diffusionimaging.ivim:OFF org.mitk.gui.qt.diffusionimaging.odfpeaks:OFF org.mitk.gui.qt.diffusionimaging.partialvolume:OFF org.mitk.gui.qt.diffusionimaging.preprocessing:OFF org.mitk.gui.qt.diffusionimaging.reconstruction:OFF org.mitk.gui.qt.diffusionimaging.registration:OFF org.mitk.gui.qt.diffusionimaging.tbss:OFF org.mitk.gui.qt.diffusionimaging.tractography:OFF org.mitk.gui.qt.diffusionimaging.python:OFF org.mitk.gui.qt.dosevisualization:OFF org.mitk.gui.qt.geometrytools:OFF org.mitk.gui.qt.igtexamples:OFF org.mitk.gui.qt.igttracking:OFF org.mitk.gui.qt.lasercontrol:OFF org.mitk.gui.qt.openigtlink:OFF org.mitk.gui.qt.imagecropper:OFF org.mitk.gui.qt.imagenavigator:ON org.mitk.gui.qt.viewnavigator:OFF org.mitk.gui.qt.materialeditor:OFF org.mitk.gui.qt.measurementtoolbox:OFF org.mitk.gui.qt.moviemaker:OFF org.mitk.gui.qt.pointsetinteraction:OFF org.mitk.gui.qt.pointsetinteractionmultispectrum:OFF org.mitk.gui.qt.python:OFF org.mitk.gui.qt.remeshing:OFF org.mitk.gui.qt.segmentation:OFF org.mitk.gui.qt.aicpregistration:OFF org.mitk.gui.qt.renderwindowmanager:OFF org.mitk.gui.qt.toftutorial:OFF org.mitk.gui.qt.tofutil:OFF org.mitk.gui.qt.tubegraph:OFF org.mitk.gui.qt.ugvisualization:OFF org.mitk.gui.qt.photoacoustics.pausviewer:OFF org.mitk.gui.qt.photoacoustics.imageprocessing:OFF org.mitk.gui.qt.photoacoustics.simulation:OFF org.mitk.gui.qt.ultrasound:OFF org.mitk.gui.qt.volumevisualization:OFF org.mitk.gui.qt.eventrecorder:OFF org.mitk.gui.qt.xnat:OFF org.mitk.gui.qt.igt.app.echotrack:OFF org.mitk.gui.qt.spectrocamrecorder:OFF org.mitk.gui.qt.classificationsegmentation:OFF org.mitk.gui.qt.overlaymanager:OFF org.mitk.gui.qt.igt.app.hummelprotocolmeasurements:OFF org.mitk.gui.qt.multilabelsegmentation:OFF org.mitk.matchpoint.core.helper:OFF org.mitk.gui.qt.matchpoint.algorithm.browser:OFF org.mitk.gui.qt.matchpoint.algorithm.control:OFF org.mitk.gui.qt.matchpoint.algorithm.batch:OFF org.mitk.gui.qt.matchpoint.mapper:OFF org.mitk.gui.qt.matchpoint.framereg:OFF org.mitk.gui.qt.matchpoint.visualizer:OFF org.mitk.gui.qt.matchpoint.evaluator:OFF org.mitk.gui.qt.matchpoint.manipulator:OFF + org.mitk.gui.qt.preprocessing.resampling:OFF org.mitk.gui.qt.cest:OFF ) diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/files.cmake b/Plugins/org.mitk.gui.qt.classificationsegmentation/files.cmake index bc0dcc0253..d058df59d8 100644 --- a/Plugins/org.mitk.gui.qt.classificationsegmentation/files.cmake +++ b/Plugins/org.mitk.gui.qt.classificationsegmentation/files.cmake @@ -1,43 +1,46 @@ set(SRC_CPP_FILES ) set(INTERNAL_CPP_FILES org_mitk_gui_qt_classificationsegmentation_Activator.cpp ClassificationSegmentation.cpp + ClassificationRegionGrow.cpp ) set(UI_FILES src/internal/ClassificationSegmentationControls.ui + src/internal/ClassificationRegionGrowControls.ui ) set(MOC_H_FILES src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.h src/internal/ClassificationSegmentation.h + src/internal/ClassificationRegionGrow.h ) # list of resource files which can be used by the plug-in # system without loading the plug-ins shared library, # for example the icon used in the menu and tabs for the # plug-in views in the workbench set(CACHED_RESOURCE_FILES resources/icon_32.png resources/icon.png plugin.xml ) # list of Qt .qrc files which contain additional resources # specific to this plugin set(QRC_FILES resources/ClassificationSegmentation.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/plugin.xml b/Plugins/org.mitk.gui.qt.classificationsegmentation/plugin.xml index 1c6fe0f4e2..0e79fbf0ac 100644 --- a/Plugins/org.mitk.gui.qt.classificationsegmentation/plugin.xml +++ b/Plugins/org.mitk.gui.qt.classificationsegmentation/plugin.xml @@ -1,11 +1,15 @@ + icon="resources/icon_32.png" /> + diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrow.cpp b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrow.cpp new file mode 100644 index 0000000000..3aa438c40a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrow.cpp @@ -0,0 +1,645 @@ +/*=================================================================== + +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. + +===================================================================*/ +// STD +#include + + +// Blueberry +#include +#include + +// Qmitk +#include "ClassificationRegionGrow.h" + +// Qt +#include +#include +#include + +//mitk image +#include +#include +#include +#include +#include +#include +#include "mitkVigraRandomForestClassifier.h" +#include "mitkCLUtil.h" +#include "qboxlayout.h" +#include + +#include "Eigen/Dense" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//#include +#include "mitkLabelSetImage.h" +#include + +#include +#include + +#include +#include +#include +#include +#include +const std::string ClassificationRegionGrow::VIEW_ID = "org.mitk.views.ClassificationRegionGrow"; + +void ClassificationRegionGrow::SetFocus() +{ + // m_Controls.buttonPerformImageProcessing->setFocus(); +} + +void ClassificationRegionGrow::CreateQtPartControl( QWidget *parent ) +{ + // create GUI widgets from the Qt Designer's .ui file + m_Controls.setupUi( parent ); + m_CalculateFeatures = true; + m_BlockManualSegmentation = false; + m_BlockPostProcessing = false; + + m_Controls.groupLearningParameter->setVisible(false); + m_Controls.groupFeatureSelection->setVisible(false); + + QmitkDataStorageComboBox * cb_inputimage = new QmitkDataStorageComboBox(this->GetDataStorage(), mitk::TNodePredicateDataType::New()); + QmitkDataStorageComboBox * cb_maskimage= new QmitkDataStorageComboBox(this->GetDataStorage(),mitk::TNodePredicateDataType::New()); + QmitkDataStorageComboBox * cb_baseimage = new QmitkDataStorageComboBox(this->GetDataStorage(), mitk::TNodePredicateDataType::New()); + + m_Controls.m_InputImageLayout->addWidget(cb_inputimage); + m_Controls.m_MaskImageLayout->addWidget(cb_maskimage); + m_Controls.StartingPointLayout->addWidget(cb_baseimage); + m_Controls.addInputButton->setIcon(QIcon::fromTheme("list-add")); + m_Controls.removeInputButton->setIcon(QIcon::fromTheme("edit-delete")); + + connect( cb_inputimage, SIGNAL(OnSelectionChanged(const mitk::DataNode*)), this, SLOT(OnInitializeSession(const mitk::DataNode*))); + connect( cb_maskimage, SIGNAL(OnSelectionChanged(const mitk::DataNode*)), this, SLOT(OnInitializeSession(const mitk::DataNode*))); + connect(m_Controls.SelectAdvancedParameter, SIGNAL(toggled(bool)), m_Controls.groupLearningParameter, SLOT(setVisible(bool))); + connect(m_Controls.SelectAdvancedParameter, SIGNAL(toggled(bool)), m_Controls.groupFeatureSelection, SLOT(setVisible(bool))); + connect(m_Controls.SelectSimpleParameters, SIGNAL(toggled(bool)), m_Controls.parameterWidget, SLOT(setVisible(bool))); + connect(m_Controls.m_DoAutomaticSecmentation, SIGNAL( clicked()), this, SLOT(DoAutomSegmentation())); + connect(m_Controls.removeInputButton, SIGNAL(clicked()), this, SLOT(RemoveItemFromLabelList())); + connect(m_Controls.addInputButton, SIGNAL(clicked()), this, SLOT(AddInputField())); + + connect(m_Controls.UseIntensity, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.Gauss1, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.Gauss2, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.Gauss3, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.Gauss4, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.Gauss5, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.DoG1, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.DoG2, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.DoG3, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.DoG4, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.DoG5, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.LoG1, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.LoG2, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.LoG3, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.LoG4, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.LoG5, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.HoG1, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.HoG2, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.HoG3, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.HoG4, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.HoG5, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.LH1, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.LH2, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.LH3, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); + connect(m_Controls.LH4, SIGNAL(toggled(bool)), this, SLOT(OnFeatureSettingsChanged())); +} + +void ClassificationRegionGrow::AddInputField() +{ + QmitkDataStorageComboBox * cb_inputimage = new QmitkDataStorageComboBox(this->GetDataStorage(), mitk::TNodePredicateDataType::New()); + //QPushButton * lockButton = new QPushButton(); + //lockButton->setText(""); + //lockButton->setMinimumWidth(40); + //lockButton->setCheckable(true); + //lockButton->setIcon(QApplication::style()->standardIcon(QStyle::SP_MediaStop)); + + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(cb_inputimage,100); + //layout->addWidget(lockButton,1); + m_Controls.m_InputImageLayout->addLayout(layout); + connect(cb_inputimage, SIGNAL(OnSelectionChanged(const mitk::DataNode*)), this, SLOT(OnInitializeSession(const mitk::DataNode*))); +} + +void ClassificationRegionGrow::RemoveItemFromLabelList() +{ + auto numberOfElements = m_Controls.m_InputImageLayout->count(); + auto lastItem = m_Controls.m_InputImageLayout->itemAt(numberOfElements-1); + QHBoxLayout *layout = dynamic_cast(lastItem); + while (QLayoutItem* item = layout->takeAt(0)) + { + if (QWidget* widget = item->widget()) + widget->deleteLater(); + delete item; + } + m_Controls.m_InputImageLayout->removeItem(lastItem); + delete lastItem; + +} + +void ClassificationRegionGrow::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*source*/, + const QList& nodes ) +{ + // iterate all selected objects, adjust warning visibility + foreach( mitk::DataNode::Pointer node, nodes ) + { + if( node.IsNotNull() && dynamic_cast(node->GetData()) ) + { + return; + } + } +} + +void ClassificationRegionGrow::OnInitializeSession(const mitk::DataNode *) +{ + OnFeatureSettingsChanged(); +} + +void ClassificationRegionGrow::ProcessFeatureImages(const mitk::Image::Pointer & raw_image, const mitk::Image::Pointer & brain_mask) +{ + typedef itk::Image DoubleImageType; + typedef itk::Image ShortImageType; + typedef itk::ConstNeighborhoodIterator NeighborhoodType; // Neighborhood iterator to access image + typedef itk::Functor::NeighborhoodFirstOrderStatistics FunctorType; + typedef itk::NeighborhoodFunctorImageFilter FOSFilerType; + typedef FOSFilerType::MaskImageType MaskImageType; + + double gaussValue = 2; + double hessianValue = 2; + double structureTensorInner = 1.8; + double structureTensorOuter = 2.4; + + // RAW + if (m_Controls.UseIntensity->isChecked()) { + m_FeatureImageVector.push_back(raw_image); + } + + // GAUSS + if (m_Controls.Gauss1->isChecked()) + { + mitk::Image::Pointer smoothed; + mitk::CLUtil::GaussianFilter(raw_image, smoothed, 1); + m_FeatureImageVector.push_back(smoothed); + } + if (m_Controls.Gauss2->isChecked()) + { + mitk::Image::Pointer smoothed; + mitk::CLUtil::GaussianFilter(raw_image, smoothed, 2); + m_FeatureImageVector.push_back(smoothed); + } + if (m_Controls.Gauss3->isChecked()) + { + mitk::Image::Pointer smoothed; + mitk::CLUtil::GaussianFilter(raw_image, smoothed, 3); + m_FeatureImageVector.push_back(smoothed); + } + if (m_Controls.Gauss4->isChecked()) + { + mitk::Image::Pointer smoothed; + mitk::CLUtil::GaussianFilter(raw_image, smoothed, 4); + m_FeatureImageVector.push_back(smoothed); + } + if (m_Controls.Gauss5->isChecked()) + { + mitk::Image::Pointer smoothed; + mitk::CLUtil::GaussianFilter(raw_image, smoothed, 5); + m_FeatureImageVector.push_back(smoothed); + } + + // Difference of Gaussian + if (m_Controls.DoG1->isChecked()) + { + mitk::Image::Pointer smoothed; + mitk::CLUtil::DifferenceOfGaussianFilter(raw_image, smoothed, 1,0.8); + m_FeatureImageVector.push_back(smoothed); + } + if (m_Controls.DoG2->isChecked()) + { + mitk::Image::Pointer smoothed; + mitk::CLUtil::DifferenceOfGaussianFilter(raw_image, smoothed, 2, 1.8); + m_FeatureImageVector.push_back(smoothed); + } + if (m_Controls.DoG3->isChecked()) + { + mitk::Image::Pointer smoothed; + mitk::CLUtil::DifferenceOfGaussianFilter(raw_image, smoothed, 3, 2.6); + m_FeatureImageVector.push_back(smoothed); + } + if (m_Controls.DoG4->isChecked()) + { + mitk::Image::Pointer smoothed; + mitk::CLUtil::DifferenceOfGaussianFilter(raw_image, smoothed, 4, 3.4); + m_FeatureImageVector.push_back(smoothed); + } + if (m_Controls.DoG5->isChecked()) + { + mitk::Image::Pointer smoothed; + mitk::CLUtil::DifferenceOfGaussianFilter(raw_image, smoothed, 5, 4.3); + m_FeatureImageVector.push_back(smoothed); + } + + // Laplacian of Gaussian + if (m_Controls.LoG1->isChecked()) + { + mitk::Image::Pointer smoothed; + mitk::CLUtil::LaplacianOfGaussianFilter(raw_image, smoothed, 1); + m_FeatureImageVector.push_back(smoothed); + } + if (m_Controls.LoG2->isChecked()) + { + mitk::Image::Pointer smoothed; + mitk::CLUtil::LaplacianOfGaussianFilter(raw_image, smoothed, 2); + m_FeatureImageVector.push_back(smoothed); + } + if (m_Controls.LoG3->isChecked()) + { + mitk::Image::Pointer smoothed; + mitk::CLUtil::LaplacianOfGaussianFilter(raw_image, smoothed, 3); + m_FeatureImageVector.push_back(smoothed); + } + if (m_Controls.LoG4->isChecked()) + { + mitk::Image::Pointer smoothed; + mitk::CLUtil::LaplacianOfGaussianFilter(raw_image, smoothed, 4); + m_FeatureImageVector.push_back(smoothed); + } + if (m_Controls.LoG5->isChecked()) + { + mitk::Image::Pointer smoothed; + mitk::CLUtil::LaplacianOfGaussianFilter(raw_image, smoothed, 5); + m_FeatureImageVector.push_back(smoothed); + } + + // Hessian of Gaussian + if (m_Controls.HoG1->isChecked()) + { + mitk::CLUtil::HessianOfGaussianFilter(raw_image, m_FeatureImageVector, 1); + } + if (m_Controls.HoG2->isChecked()) + { + mitk::CLUtil::HessianOfGaussianFilter(raw_image, m_FeatureImageVector, 2); + } + if (m_Controls.HoG3->isChecked()) + { + mitk::CLUtil::HessianOfGaussianFilter(raw_image, m_FeatureImageVector, 3); + } + if (m_Controls.HoG4->isChecked()) + { + mitk::CLUtil::HessianOfGaussianFilter(raw_image, m_FeatureImageVector, 4); + } + if (m_Controls.HoG5->isChecked()) + { + mitk::CLUtil::HessianOfGaussianFilter(raw_image, m_FeatureImageVector, 5); + } + + // LocalHistogram + if (m_Controls.LH1->isChecked()) + { + mitk::CLUtil::LocalHistogram(raw_image, m_FeatureImageVector, 5,3); + } + if (m_Controls.LH2->isChecked()) + { + mitk::CLUtil::LocalHistogram(raw_image, m_FeatureImageVector, 5, 5); + } + if (m_Controls.LH3->isChecked()) + { + mitk::CLUtil::LocalHistogram(raw_image, m_FeatureImageVector, 10, 3); + } + if (m_Controls.LH4->isChecked()) + { + mitk::CLUtil::LocalHistogram(raw_image, m_FeatureImageVector, 10, 5); + } +} + +void ClassificationRegionGrow::OnFeatureSettingsChanged() +{ + MITK_INFO << "FeatureSettingsChanged"; + m_CalculateFeatures = true; +} + +void ClassificationRegionGrow::DoAutomSegmentation() +{ + MITK_INFO << "Start Automatic Segmentation ..."; + // Load Images from registration process + QmitkDataStorageComboBox * cb_image = dynamic_cast(m_Controls.m_InputImageLayout->itemAt(1)->widget()); + QmitkDataStorageComboBox * cb_maskimage = dynamic_cast(m_Controls.m_MaskImageLayout->itemAt(1)->widget()); + mitk::Image::Pointer raw_image; + mitk::Image::Pointer mask_image; + if ((cb_image != NULL) || (cb_maskimage != NULL)) + { + raw_image = dynamic_cast(cb_image->GetSelectedNode()->GetData()); + mask_image = dynamic_cast(cb_maskimage->GetSelectedNode()->GetData()); + } + else { + QMessageBox msgBox; + msgBox.setText("Please specify the images that shlould be used."); + msgBox.exec(); + return; + } + + if (raw_image.IsNull() || mask_image.IsNull()) + { + QMessageBox msgBox; + msgBox.setText("Error during processing the specified images."); + msgBox.exec(); + return; + } + + std::vector imageList; + imageList.push_back(raw_image); + for (int i = 2; i < m_Controls.m_InputImageLayout->count(); ++i) + { + QLayout* layout = dynamic_cast(m_Controls.m_InputImageLayout->itemAt(i)); + MITK_INFO << layout; + QmitkDataStorageComboBox * tmp_cb_image = dynamic_cast(layout->itemAt(0)->widget()); + MITK_INFO << tmp_cb_image; + if (tmp_cb_image) + { + mitk::Image::Pointer tmp_image = dynamic_cast(tmp_cb_image); + if (tmp_image.IsNotNull()) + { + MITK_INFO << "Adding Image..."; + imageList.push_back(tmp_image); + } + } + } + + MITK_INFO << "Start Feature Calculation ..."; + if(m_CalculateFeatures) + { + m_FeatureImageVector.clear(); + for (auto img : imageList) + { + ProcessFeatureImages(img, mask_image); + } + m_CalculateFeatures = false; + if (m_Controls.checkAddFeaturesToDataManager->isChecked()) + { + for (int i = 0; i < m_FeatureImageVector.size(); ++i) + { + auto newName = "Feature_" + std::to_string(i); + AddAsDataNode(m_FeatureImageVector[i].GetPointer(), newName); + } + } + } + + MITK_INFO << "Start Classifier Training ..."; + TrainClassifier(raw_image, mask_image); + + MITK_INFO << "Predict extended Segmentation ..."; + PredictSegmentation(raw_image, mask_image); +} + + +void ClassificationRegionGrow::TrainClassifier(const mitk::Image::Pointer & raw_image, const mitk::Image::Pointer & mask_image) +{ + typedef itk::Image DoubleImageType; + typedef itk::Image ShortImageType; + typedef itk::ConstNeighborhoodIterator NeighborhoodType; // Neighborhood iterator to access image + typedef itk::Functor::NeighborhoodFirstOrderStatistics FunctorType; + typedef itk::NeighborhoodFunctorImageFilter FOSFilerType; + + DoubleImageType::Pointer input; + ShortImageType::Pointer mask; + mitk::CastToItkImage(raw_image, input); + mitk::CastToItkImage(mask_image, mask); + + int numberOfSegmentedVoxel = 0; + int numberOfFeatures = m_FeatureImageVector.size(); + + auto maskIter = itk::ImageRegionConstIteratorWithIndex(mask, mask->GetLargestPossibleRegion()); + m_SegmentedLocations.clear(); + m_SegmentedOrganLocations.clear(); + + MITK_INFO << "Count Segmentation Size ..."; + while ( ! maskIter.IsAtEnd()) + { + if (maskIter.Value() > 0) + { + m_SegmentedLocations.push_back(maskIter.GetIndex()); + numberOfSegmentedVoxel++; + if (maskIter.Value() > 1) + { + m_SegmentedOrganLocations.push_back(maskIter.GetIndex()); + } + } + ++maskIter; + } + MITK_INFO << "Sizes: " << numberOfSegmentedVoxel << " " << m_SegmentedOrganLocations.size(); + + Eigen::MatrixXi Y_train = mitk::CLUtil::Transform(mask_image, mask_image); + Eigen::MatrixXd X_train = Eigen::MatrixXd(numberOfSegmentedVoxel, numberOfFeatures); + unsigned int index = 0; + + MITK_INFO << "Convert Training Data to Eigen Matrix ..."; + for (const auto & image : m_FeatureImageVector) + { + X_train.col(index) = mitk::CLUtil::Transform(image, mask_image); + ++index; + } + + MITK_INFO << "Classifier Training ..."; + m_Classifier = mitk::VigraRandomForestClassifier::New(); + //this->m_Controls.Maximum + m_Classifier->SetTreeCount(m_Controls.NumberOfTrees->value()); + m_Classifier->SetSamplesPerTree(m_Controls.SamplesPerTree->value()); + m_Classifier->SetMinimumSplitNodeSize(m_Controls.MinimumSamplesPerNode->value()); + m_Classifier->SetMaximumTreeDepth(m_Controls.MaximumTreeDepth->value()); + m_Classifier->Train(X_train, Y_train); +} + +static void addNeighbours(std::stack > &stack, itk::Index<3> idx) +{ + idx[0] -= 1; + stack.push(idx); + idx[0] += 2; + stack.push(idx); + idx[0] -= 1; + idx[1] -= 1; + stack.push(idx); + idx[1] += 2; + stack.push(idx); + idx[1] -= 1; + idx[2] -= 1; + stack.push(idx); + idx[2] += 2; + stack.push(idx); +} + +void ClassificationRegionGrow::PredictSegmentation(const mitk::Image::Pointer & raw_image, const mitk::Image::Pointer & mask_image) +{ + typedef itk::Image DoubleImageType; + typedef itk::Image ShortImageType; + + DoubleImageType::Pointer input; + ShortImageType::Pointer mask; + mitk::CastToItkImage(raw_image, input); + mitk::CastToItkImage(mask_image, mask); + + std::vector featureImages; + for (auto fimage : m_FeatureImageVector) + { + DoubleImageType::Pointer feature; + mitk::CastToItkImage(fimage, feature); + featureImages.push_back(feature); + } + + ShortImageType::Pointer usedLocation = ShortImageType::New(); + usedLocation->SetRegions(mask->GetLargestPossibleRegion()); + usedLocation->Allocate(); + usedLocation->FillBuffer(0); + + ShortImageType::Pointer resultSegmentation = ShortImageType::New(); + if (m_Controls.UpdateImage->isChecked()) { + QmitkDataStorageComboBox * cb_maskimage = dynamic_cast(m_Controls.StartingPointLayout->itemAt(2)->widget()); + mitk::Image::Pointer base_image = dynamic_cast(cb_maskimage->GetSelectedNode()->GetData()); + mitk::CastToItkImage(base_image, resultSegmentation); + } + else { + + resultSegmentation->SetRegions(mask->GetLargestPossibleRegion()); + resultSegmentation->Allocate(); + if (m_Controls.SegmentBackground->isChecked()) + { + resultSegmentation->FillBuffer(1); + } + else { + resultSegmentation->FillBuffer(0); + } + } + + // Fill list of Stacks + std::vector > > listOfStacks; + while (m_SegmentedOrganLocations.size() > 0) + { + auto currentLocation = m_SegmentedOrganLocations.back(); + m_SegmentedOrganLocations.pop_back(); + auto cValue = mask->GetPixel(currentLocation); + resultSegmentation->SetPixel(currentLocation, cValue); + usedLocation->SetPixel(currentLocation, 1000); + while (listOfStacks.size() < cValue+1) + { + listOfStacks.push_back(std::stack >()); + } + addNeighbours(listOfStacks[cValue],currentLocation); + } + + + int countPredicted = 0; + bool connectAllLabels = m_Controls.localGrowing->isChecked(); + //m_SegmentedOrganLocations.reserve(10000); + + Eigen::MatrixXd currentX = Eigen::MatrixXd(1, featureImages.size()); + vigra::MultiArrayView<2, double> X(vigra::Shape2(currentX.rows(), currentX.cols()), currentX.data()); + auto outLabel = Eigen::MatrixXi(currentX.rows(), 1); + vigra::MultiArrayView<2, int> Y(vigra::Shape2(currentX.rows(), 1), outLabel.data()); + for (int i = 2; i < listOfStacks.size(); ++i) + { + while (listOfStacks[i].size() > 0) + { + auto currentLocation = listOfStacks[i].top(); + listOfStacks[i].pop(); + if (!mask->GetLargestPossibleRegion().IsInside(currentLocation)) + { + continue; + } + if (usedLocation->GetPixel(currentLocation) > i) + { + continue; + } + usedLocation->SetPixel(currentLocation, i+1); + + + for (int f = 0; f < featureImages.size(); ++f) + { + currentX(0, f) = featureImages[f]->GetPixel(currentLocation); + } + + m_Classifier->GetRandomForest().predictLabels(X, Y); + ++countPredicted; + if ((Y(0, 0) == i) || + ((Y(0, 0) > 1) && (connectAllLabels))) + { + resultSegmentation->SetPixel(currentLocation, Y(0, 0)); + addNeighbours(listOfStacks[i], currentLocation); + } + } + } + MITK_INFO << "Number of Predictions: " << countPredicted; + + MITK_INFO << "Finished Segmentation..."; + mitk::Image::Pointer result; + mitk::CastToMitkImage(resultSegmentation, result); + result->SetOrigin(raw_image->GetGeometry()->GetOrigin()); + result->SetSpacing(raw_image->GetGeometry()->GetSpacing()); + mitk::LabelSetImage::Pointer labelResult = mitk::LabelSetImage::New(); + labelResult->InitializeByLabeledImage(result); + mitk::LabelSetImage::Pointer oldLabelSet = dynamic_cast(mask_image.GetPointer()); + labelResult->AddLabelSetToLayer(labelResult->GetActiveLayer(),oldLabelSet->GetLabelSet()); + MITK_INFO << "Passing Back..."; + AddAsDataNode(labelResult.GetPointer(), "ResultSegmentation"); +} + +mitk::DataNode::Pointer ClassificationRegionGrow::AddAsDataNode(const mitk::BaseData::Pointer & data_, const std::string & name ) +{ + mitk::DataNode::Pointer node = nullptr; + node = this->GetDataStorage()->GetNamedNode(name); + + if(node.IsNull()) + { + node = mitk::DataNode::New(); + node->SetData(data_); + node->SetName(name); + this->GetDataStorage()->Add(node); + }else{ + + if(dynamic_cast(node->GetData()) && dynamic_cast(data_.GetPointer())) + { + mitk::Image::Pointer target_image = dynamic_cast(node->GetData()); + mitk::Image::Pointer source_image = dynamic_cast(data_.GetPointer()); + mitk::ImageReadAccessor ra(source_image); + target_image->SetImportVolume(const_cast(ra.GetData())); + this->RequestRenderWindowUpdate(); + } + + if(dynamic_cast(node->GetData()) && dynamic_cast(data_.GetPointer())) + { + node->SetData(data_); + node->Modified(); + } + + } + + return node; +} diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrow.h b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrow.h new file mode 100644 index 0000000000..4d8c917aae --- /dev/null +++ b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrow.h @@ -0,0 +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. + +===================================================================*/ + +#ifndef ClassificationRegionGrow_h +#define ClassificationRegionGrow_h + +#include + +#include + +#include "ui_ClassificationRegionGrowControls.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "QmitkPointListViewWidget.h" +#include + +#include +/** +\brief ClassificationRegionGrow + +\warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. + +\sa QmitkAbstractView +\ingroup ${plugin_target}_internal +*/ + + +//class QmitkPointListWidget; +class ctkSliderWidget; +class ClassificationRegionGrow : public QmitkAbstractView +{ + // this is needed for all Qt objects that should have a Qt meta-object + // (everything that derives from QObject and wants to have signal/slots) + Q_OBJECT + +public: + + static const std::string VIEW_ID; + + bool m_CalculateFeatures; + + std::vector m_FeatureImageVector; + std::vector m_ResultImageVector; + + bool m_BlockManualSegmentation; + QFutureWatcher> m_ManualSegmentationFutureWatcher; + + bool m_BlockPostProcessing; + QFutureWatcher> m_PostProcessingFutureWatcher; + + public slots: + + /// \brief Called when the user clicks the GUI button + void DoAutomSegmentation(); + void AddInputField(); + + void RemoveItemFromLabelList(); + + void OnFeatureSettingsChanged(); + void OnInitializeSession(const mitk::DataNode*); + +protected: + std::vector > m_SegmentedLocations; + std::vector > m_SegmentedOrganLocations; + + + + typedef float MeasurementType; + typedef itk::Statistics::Histogram< MeasurementType, + itk::Statistics::DenseFrequencyContainer2 > HistogramType; + + + virtual void CreateQtPartControl(QWidget *parent) override; + virtual void SetFocus() override; + + mitk::DataNode::Pointer AddAsDataNode(const mitk::BaseData::Pointer & data_, const std::string & name ); + + void ProcessFeatureImages(const mitk::Image::Pointer & raw_image, const mitk::Image::Pointer & mask_image); + void TrainClassifier(const mitk::Image::Pointer & raw_image, const mitk::Image::Pointer & mask_image); + void PredictSegmentation(const mitk::Image::Pointer & raw_image, const mitk::Image::Pointer & mask_image); + + /// \brief called by QmitkFunctionality when DataManager's selection has changed + virtual void OnSelectionChanged( berry::IWorkbenchPart::Pointer source, + const QList& nodes ) override; + + Ui::ClassificationRegionGrowControls m_Controls; + + mitk::VigraRandomForestClassifier::Pointer m_Classifier; +}; + +#endif // ClassificationRegionGrow_h diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrowControls.ui b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrowControls.ui new file mode 100644 index 0000000000..ad002f99b0 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/ClassificationRegionGrowControls.ui @@ -0,0 +1,593 @@ + + + ClassificationRegionGrowControls + + + + 0 + 0 + 352 + 979 + + + + + 0 + 0 + + + + QmitkTemplate + + + + + + + 17 + 75 + true + false + false + PreferAntialias + true + + + + Classifier Region Growing + + + + + + + + + + true + + + + Basic Image + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + false + + + false + + + + + + + + + + + + + + + + + + + true + + + + Segmentation Image + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 0 + 64 + + + + + Segoe UI + 14 + 75 + true + + + + Update Segmentation + + + + :/ClassificationSegmentation/button_process_2.png:/ClassificationSegmentation/button_process_2.png + + + + 32 + 32 + + + + Ctrl+A + + + + + + + Parameters + + + true + + + + + + + + + Segment Background (First Label) + + + + + + + Connectness for all labels + + + + + + + + + + + + + + + + Use existing segmentation as starting point + + + + + + + + + + + + + + + Advanced Parameter + + + true + + + false + + + + + + Learning Parameters + + + + + + Samples per Tree: + + + + + + + 0.010000000000000 + + + 1.000000000000000 + + + 0.100000000000000 + + + 0.660000000000000 + + + + + + + Number of Trees: + + + + + + + 1 + + + 2000 + + + 10 + + + + + + + Minimum Samples per Node: + + + + + + + 1 + + + 200000 + + + 5 + + + + + + + Maximum Tree depth: + + + + + + + 1 + + + 2000000 + + + 100 + + + + + + + + + + Feature Selection + + + false + + + + + + 4 + + + + + + + 3 + + + + + + + Laplacian of Gauss: + + + + + + + 5 + + + + + + + Intensity Value: + + + + + + + 4 + + + + + + + ( Neigh. Size / Bins) + + + + + + + 3 + + + + + + + 2 + + + + + + + Add Feature Images to DataManager + + + + + + + Diff. of Gauss: + + + + + + + 4 + + + + + + + 1 + + + + + + + 1 + + + + + + + 5 + + + + + + + 3 + + + + + + + + + + true + + + + + + + 5 + + + + + + + 1 + + + + + + + Hessian of Gaussian: + + + + + + + 2 + + + + + + + Local Histogram + + + + + + + 2 + + + true + + + + + + + Gaussian smoothed: + + + + + + + 1 + + + + + + + 2 + + + + + + + 4 + + + + + + + 3 + + + + + + + 5 + + + + + + + 3/5 + + + + + + + 5/5 + + + + + + + 3/10 + + + + + + + 5/10 + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.cpp b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationregiongrow_Activator.cpp similarity index 60% copy from Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.cpp copy to Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationregiongrow_Activator.cpp index 3fb6fb829a..fa6ce8555f 100644 --- a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.cpp +++ b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationregiongrow_Activator.cpp @@ -1,33 +1,33 @@ /*=================================================================== 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 "org_mitk_gui_qt_classificationsegmentation_Activator.h" -#include "ClassificationSegmentation.h" +#include "org_mitk_gui_qt_classificationregiongrow_Activator.h" +#include "ClassificationRegionGrow.h" namespace mitk { -void org_mitk_gui_qt_classificationsegmentation_Activator::start(ctkPluginContext* context) +void org_mitk_gui_qt_classificationregiongrow_Activator::start(ctkPluginContext* context) { - BERRY_REGISTER_EXTENSION_CLASS(ClassificationSegmentation, context) + BERRY_REGISTER_EXTENSION_CLASS(ClassificationRegionGrow, context) } -void org_mitk_gui_qt_classificationsegmentation_Activator::stop(ctkPluginContext* context) +void org_mitk_gui_qt_classificationregiongrow_Activator::stop(ctkPluginContext* context) { Q_UNUSED(context) } } diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.h b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationregiongrow_Activator.h similarity index 66% copy from Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.h copy to Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationregiongrow_Activator.h index 02fc27aa6f..3d5e22157c 100644 --- a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.h +++ b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationregiongrow_Activator.h @@ -1,41 +1,41 @@ /*=================================================================== 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 org_mitk_gui_qt_classificationsegmentation_Activator_h -#define org_mitk_gui_qt_classificationsegmentation_Activator_h +#ifndef org_mitk_gui_qt_classificationregiongrow_Activator_h +#define org_mitk_gui_qt_classificationregiongrow_Activator_h #include namespace mitk { -class org_mitk_gui_qt_classificationsegmentation_Activator : +class org_mitk_gui_qt_classificationregiongrow_Activator : public QObject, public ctkPluginActivator { Q_OBJECT - Q_PLUGIN_METADATA(IID "org_mitk_gui_qt_classificationsegmentation") + Q_PLUGIN_METADATA(IID "org_mitk_gui_qt_classificationregiongrow") Q_INTERFACES(ctkPluginActivator) public: void start(ctkPluginContext* context); void stop(ctkPluginContext* context); -}; // org_mitk_gui_qt_classificationsegmentation_Activator +}; // org_mitk_gui_qt_classificationregiongrow_Activator } -#endif // org_mitk_gui_qt_classificationsegmentation_Activator_h +#endif // org_mitk_gui_qt_classificationregiongrow_Activator_h diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.cpp b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.cpp index 3fb6fb829a..c796fd6af2 100644 --- a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.cpp +++ b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.cpp @@ -1,33 +1,35 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "org_mitk_gui_qt_classificationsegmentation_Activator.h" #include "ClassificationSegmentation.h" +#include "ClassificationRegionGrow.h" namespace mitk { void org_mitk_gui_qt_classificationsegmentation_Activator::start(ctkPluginContext* context) { BERRY_REGISTER_EXTENSION_CLASS(ClassificationSegmentation, context) + BERRY_REGISTER_EXTENSION_CLASS(ClassificationRegionGrow, context) } void org_mitk_gui_qt_classificationsegmentation_Activator::stop(ctkPluginContext* context) { Q_UNUSED(context) } } diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.h b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.h index 02fc27aa6f..f6d899f134 100644 --- a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.h +++ b/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.h @@ -1,41 +1,42 @@ /*=================================================================== 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 org_mitk_gui_qt_classificationsegmentation_Activator_h #define org_mitk_gui_qt_classificationsegmentation_Activator_h #include namespace mitk { class org_mitk_gui_qt_classificationsegmentation_Activator : public QObject, public ctkPluginActivator { Q_OBJECT Q_PLUGIN_METADATA(IID "org_mitk_gui_qt_classificationsegmentation") +// Q_PLUGIN_METADATA(IID "org_mitk_gui_qt_classificationregiongrow") Q_INTERFACES(ctkPluginActivator) public: void start(ctkPluginContext* context); void stop(ctkPluginContext* context); }; // org_mitk_gui_qt_classificationsegmentation_Activator } #endif // org_mitk_gui_qt_classificationsegmentation_Activator_h diff --git a/Plugins/org.mitk.gui.qt.preprocessing.resampling/CMakeLists.txt b/Plugins/org.mitk.gui.qt.preprocessing.resampling/CMakeLists.txt new file mode 100644 index 0000000000..cd46837ebb --- /dev/null +++ b/Plugins/org.mitk.gui.qt.preprocessing.resampling/CMakeLists.txt @@ -0,0 +1,9 @@ + +project(org_mitk_gui_qt_preprocessing_resampling) + +mitk_create_plugin( + EXPORT_DIRECTIVE PREPROCESSING_RESAMPLING_EXPORT + EXPORTED_INCLUDE_SUFFIXES src + MODULE_DEPENDS MitkQtWidgetsExt MitkMapperExt MitkImageDenoising + PACKAGE_DEPENDS ITK|ITKMathematicalMorphology +) diff --git a/Plugins/org.mitk.gui.qt.preprocessing.resampling/documentation/UserManual/QmitkBasicImageProcessing.dox b/Plugins/org.mitk.gui.qt.preprocessing.resampling/documentation/UserManual/QmitkBasicImageProcessing.dox new file mode 100644 index 0000000000..bff25497fa --- /dev/null +++ b/Plugins/org.mitk.gui.qt.preprocessing.resampling/documentation/UserManual/QmitkBasicImageProcessing.dox @@ -0,0 +1,126 @@ +/** +\page org_mitk_views_basicimageprocessing The Basic Image Processing Plugin + +\imageMacro{QmitkBasicImageProcessing_ImageProcessing_48.png,"Icon of the Basic Image Processing Plugin",2.00} + +\tableofcontents + +\section QmitkBasicImageProcessingUserManualSummary Summary + +This view provides an easy interface to fundamental image preprocessing and enhancement filters. +It offers filter operations on 3D and 4D images in the areas of noise suppression, morphological operations, edge detection and image arithmetics, +as well as image inversion and downsampling. + +Please see \ref QmitkBasicImageProcessingUserManualOverview for more detailed information on usage and supported filters. +If you encounter problems using the view, please have a look at the \ref QmitkBasicImageProcessingUserManualTrouble page. + +\section QmitkBasicImageProcessingUserManualOverview Overview + +This view provides an easy interface to fundamental image preprocessing and image enhancement filters. +It offers a variety of filter operations in the areas of noise suppression, morphological operations, edge detection and image arithmetics. +Currently the view can be used with all 3D and 4D image types loadable by MITK. +2D image support will be added in the future. +All filters are encapsulated from the Insight Segmentation and Registration Toolkit (ITK, www.itk.org). + +\imageMacro{QmitkBasicImageProcessing_BIP_Overview.png,"MITK with the Basic Image Processing view",16.00} + +This document will tell you how to use this view, but it is assumed that you already know how to use MITK in general. + +\section QmitkBasicImageProcessingUserManualFilters Filters + +This section will not describe the fundamental functioning of the single filters in detail, though. +If you want to know more about a single filter, please have a look at http://www.itk.org/Doxygen316/html/classes.html +or in any good digital image processing book. For total denoising filter, please see Tony F. Chan et al., "The digital TV filter and nonlinear denoising". + +Available filters are: + +

\a Single image operations

+ +
    +
  • Noise Suppression
  • +
      +
    • Gaussian Denoising
    • +
    • Median Filtering
    • +
    • Total Variation Denoising
    • +
    + +
  • Morphological Operations
  • +
      +
    • Dilation
    • +
    • Erosion
    • +
    • Opening
    • +
    • Closing
    • +
    + +
  • %Edge Detection
  • +
      +
    • Gradient Image
    • +
    • Laplacian Operator (Second Derivative)
    • +
    • Sobel Operator
    • +
    + +
  • Misc
  • +
      +
    • Threshold
    • +
    • Image Inversion
    • +
    • Downsampling (isotropic)
    • +
    +
+ +

\a Dual image operations

+ +
    +
  • Image Arithmetics
  • +
      +
    • Add two images
    • +
    • Subtract two images
    • +
    • Multiply two images
    • +
    • Divide two images
    • +
    + +
  • Binary Operations
  • +
      +
    • Logical AND
    • +
    • Logical OR
    • +
    • Logical XOR
    • +
    +
+ +\section QmitkBasicImageProcessingUserManualUsage Usage + +All you have to do to use a filter is to: +
    +
  • Load an image into MITK
  • +
  • Select it in data manager +
  • Select which filter you want to use via the drop down list
  • +
  • Press the execute button
  • +
+A busy cursor appeares; when it vanishes, the operation is completed. Your filtered image is displayed and selected for further processing. +(If the checkbox "Hide original image" is not selected, you will maybe not see the filter result imideately, +because your filtered image is possibly hidden by the original.) + +For two image operations, please make sure that the correct second image is selected in the drop down menu, and the image order is correct. +For sure, image order only plays a role for image subtraction and division. These are conducted (Image1 - Image2) or (Image1 / Image2), respectively. + +Please Note: When you select a 4D image, you can select the time step for the filter to work on via the time slider at the top of the GUI. +The 3D image at this time step is extracted and processed. The result will also be a 3D image. +This means, a true 4D filtering is not yet supported. + +\section QmitkBasicImageProcessingUserManualTrouble Troubleshooting + +I get an error when using a filter on a 2D image.
+2D images are not yet supported... + +I use a filter on a 4D image, and the output is 3D.
+When you select a 4D image, you can select the time step for the filter to work on via the time slider at the top of the GUI. +The 3D image at this time step is extracted and processed. The result will also be a 3D image. +This means, a true 4D filtering is not supported by now. + +A filter crashes during execution.
+Maybe your image is too large. Some filter operations, like derivatives, take a lot of memory. +Try downsampling your image first. + +All other problems.
+Please report to the MITK mailing list. +See http://www.mitk.org/wiki/Mailinglist on how to do this. +*/ diff --git a/Plugins/org.mitk.gui.qt.preprocessing.resampling/documentation/UserManual/QmitkBasicImageProcessing_BIP_Overview.png b/Plugins/org.mitk.gui.qt.preprocessing.resampling/documentation/UserManual/QmitkBasicImageProcessing_BIP_Overview.png new file mode 100644 index 0000000000..3515cb8e1e Binary files /dev/null and b/Plugins/org.mitk.gui.qt.preprocessing.resampling/documentation/UserManual/QmitkBasicImageProcessing_BIP_Overview.png differ diff --git a/Plugins/org.mitk.gui.qt.preprocessing.resampling/documentation/UserManual/QmitkBasicImageProcessing_ImageProcessing_48.png b/Plugins/org.mitk.gui.qt.preprocessing.resampling/documentation/UserManual/QmitkBasicImageProcessing_ImageProcessing_48.png new file mode 100644 index 0000000000..27dc941ac4 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.preprocessing.resampling/documentation/UserManual/QmitkBasicImageProcessing_ImageProcessing_48.png differ diff --git a/Plugins/org.mitk.gui.qt.preprocessing.resampling/documentation/doxygen/modules.dox b/Plugins/org.mitk.gui.qt.preprocessing.resampling/documentation/doxygen/modules.dox new file mode 100644 index 0000000000..7bf036aa2a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.preprocessing.resampling/documentation/doxygen/modules.dox @@ -0,0 +1,16 @@ +/** + \defgroup org_mitk_gui_qt_basicimageprocessing org.mitk.gui.qt.basicimageprocessing + \ingroup MITKPlugins + + \brief Describe your plugin here. + +*/ + +/** + \defgroup org_mitk_gui_qt_basicimageprocessing_internal Internal + \ingroup org_mitk_gui_qt_basicimageprocessing + + \brief This subcategory includes the internal classes of the org.mitk.gui.qt.basicimageprocessing plugin. Other + plugins must not rely on these classes. They contain implementation details and their interface + may change at any time. We mean it. +*/ diff --git a/Plugins/org.mitk.gui.qt.preprocessing.resampling/files.cmake b/Plugins/org.mitk.gui.qt.preprocessing.resampling/files.cmake new file mode 100644 index 0000000000..8c61cd6c08 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.preprocessing.resampling/files.cmake @@ -0,0 +1,35 @@ +set(SRC_CPP_FILES + +) + +set(INTERNAL_CPP_FILES + QmitkPreprocessingResamplingView.cpp + mitkPreprocessingResamplingActivator.cpp +) + +set(UI_FILES + src/internal/QmitkPreprocessingResamplingViewControls.ui +) + +set(MOC_H_FILES + src/internal/QmitkPreprocessingResamplingView.h + src/internal/mitkPreprocessingResamplingActivator.h +) + +set(CACHED_RESOURCE_FILES + resources/lena.xpm + plugin.xml +) + +set(QRC_FILES + resources/QmitkPreprocessingResamplingView.qrc +) + +foreach(file ${SRC_CPP_FILES}) + set(CPP_FILES ${CPP_FILES} src/${file}) +endforeach(file ${SRC_CPP_FILES}) + +foreach(file ${INTERNAL_CPP_FILES}) + set(CPP_FILES ${CPP_FILES} src/internal/${file}) +endforeach(file ${INTERNAL_CPP_FILES}) + diff --git a/Plugins/org.mitk.gui.qt.preprocessing.resampling/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.preprocessing.resampling/manifest_headers.cmake new file mode 100644 index 0000000000..0eba5ba0eb --- /dev/null +++ b/Plugins/org.mitk.gui.qt.preprocessing.resampling/manifest_headers.cmake @@ -0,0 +1,5 @@ +set(Plugin-Name "MITK Preprocessing Resampling") +set(Plugin-Version "1.0") +set(Plugin-Vendor "DKFZ; Medical and Biological Informatics") +set(Plugin-ContactAddress "http://www.mitk.org") +set(Require-Plugin org.mitk.gui.qt.common.legacy) diff --git a/Plugins/org.mitk.gui.qt.preprocessing.resampling/plugin.xml b/Plugins/org.mitk.gui.qt.preprocessing.resampling/plugin.xml new file mode 100644 index 0000000000..98e852ca8a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.preprocessing.resampling/plugin.xml @@ -0,0 +1,20 @@ + + + + + + Resampling an Image to an specific sizes + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.preprocessing.resampling/resources/ImageProcessing_64.xpm b/Plugins/org.mitk.gui.qt.preprocessing.resampling/resources/ImageProcessing_64.xpm new file mode 100644 index 0000000000..9265a814a2 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.preprocessing.resampling/resources/ImageProcessing_64.xpm @@ -0,0 +1,318 @@ +/* XPM */ +static char *ImageProcessing___[] = { +/* columns rows colors chars-per-pixel */ +"64 64 248 2", +" c #3F3235", +". c #572F3A", +"X c #592F3A", +"o c #463337", +"O c #483137", +"+ c #513037", +"@ c #503138", +"# c #503039", +"$ c #533038", +"% c #533138", +"& c #533139", +"* c #553039", +"= c #5B313C", +"- c #612F3B", +"; c #622E3B", +": c #632F3B", +"> c #652F3C", +", c #682E3C", +"< c #6F2D3E", +"1 c #712E3E", +"2 c #752D3E", +"3 c #742C3F", +"4 c #752C3F", +"5 c #7A2D40", +"6 c #6E3F4A", +"7 c #743F4D", +"8 c #773F4C", +"9 c #7B3D4C", +"0 c #783E4C", +"q c #7B3E4D", +"w c #7D3D4C", +"e c gray30", +"r c #5B444B", +"t c #67434C", +"y c #67434D", +"u c #62454C", +"i c #62464D", +"p c #62474D", +"a c #62464E", +"s c #65444C", +"d c #65454C", +"f c #66444C", +"g c #67444C", +"h c #66444D", +"j c #67444D", +"k c #66454D", +"l c #67454D", +"z c #64464D", +"x c #66464D", +"c c #67454E", +"v c #66464F", +"b c #68434C", +"n c #6E404C", +"m c #6F414D", +"M c #6D424C", +"N c #6E424C", +"B c #68444C", +"V c #69444C", +"C c #68454D", +"Z c #70414C", +"A c #71414C", +"S c #71404D", +"D c #70414D", +"F c #72404C", +"G c #73404D", +"H c #744651", +"J c #7D4351", +"K c #7D4F59", +"L c #665659", +"P c #615B5C", +"I c gray40", +"U c #676767", +"Y c #686868", +"T c DimGray", +"R c #6A6A6A", +"E c gray42", +"W c #6C6B6C", +"Q c #6C6C6C", +"! c #6D6D6D", +"~ c #717171", +"^ c #727272", +"/ c gray45", +"( c #747474", +") c gray46", +"_ c #767676", +"` c #777777", +"' c gray47", +"] c gray48", +"[ c #7B7B7B", +"{ c #7C7C7C", +"} c #852B41", +"| c #822D40", +" . c #8A2B42", +".. c #8C2B42", +"X. c #8E2A42", +"o. c #892C42", +"O. c #962B45", +"+. c #9E2A46", +"@. c #8D364C", +"#. c #833A4B", +"$. c #803B4C", +"%. c #823A4C", +"&. c #853A4C", +"*. c #863A4C", +"=. c #863A4D", +"-. c #813C4C", +";. c #833C4D", +":. c #88394C", +">. c #8B384C", +",. c #97344B", +"<. c #90364C", +"1. c #92374D", +"2. c #94344C", +"3. c #98334B", +"4. c #9F324B", +"5. c #9F304C", +"6. c #9D324C", +"7. c #AD2748", +"8. c #A22F4B", +"9. c #A42F4B", +"0. c #A72E4B", +"q. c #AF2848", +"w. c #AB2D4B", +"e. c #AC2D4B", +"r. c #AA2D4C", +"t. c #AD2C4C", +"y. c #B6274A", +"u. c #B7274A", +"i. c #B8274A", +"p. c #BB274B", +"a. c #BD264B", +"s. c #BD274B", +"d. c #BE264B", +"f. c #BF264B", +"g. c #BE274B", +"h. c #BF274B", +"j. c #B32A4B", +"k. c #B22B4B", +"l. c #B7284B", +"z. c #B6294B", +"x. c #B42A4B", +"c. c #B52A4B", +"v. c #B42A4C", +"b. c #B62A4C", +"n. c #B9284B", +"m. c #BA284B", +"M. c #BB284B", +"N. c #B9294C", +"B. c #A1304C", +"V. c #A2304C", +"C. c #B73152", +"Z. c #AB445D", +"A. c #825962", +"S. c #9D5667", +"D. c #95606E", +"F. c #95616E", +"G. c #93646F", +"H. c #8F6A72", +"J. c #8B7077", +"K. c #8C7077", +"L. c #8D777D", +"P. c #88797D", +"I. c #937077", +"U. c #907A7F", +"Y. c #897D81", +"T. c #8A7D80", +"R. c #8A7D81", +"E. c #8B7E81", +"W. c #8C7D82", +"Q. c #A87C85", +"!. c #808080", +"~. c #818181", +"^. c gray51", +"/. c #838383", +"(. c #848081", +"). c #868084", +"_. c #848484", +"`. c gray52", +"'. c gray53", +"]. c #8C8485", +"[. c #8D8485", +"{. c #8E8486", +"}. c #8F8487", +"|. c #8A8588", +" X c #8E8789", +".X c #888888", +"XX c #898989", +"oX c gray54", +"OX c #8F8889", +"+X c gray55", +"@X c #8D8C8D", +"#X c #8D8D8D", +"$X c #918586", +"%X c #908587", +"&X c #918587", +"*X c #928588", +"=X c #938588", +"-X c #96878A", +";X c #97878B", +":X c #918E8F", +">X c #99888B", +",X c #9A888B", +".f.f.f.s.9 FXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXg 0.f.f.f.j.M FXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX7 n.f.f.f.V.j FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXa @.f.f.f.h.=.FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXy r.f.f.f.b.S FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX8 m.f.f.f.B.c FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXi 1.f.f.f.h.&.FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXB w.f.f.f.v.D FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX0 M.f.f.f.4.h FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXu 2.f.f.f.g.;.FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXb t.f.f.f.j.N FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXq p.f.f.f.6.C FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXh 3.f.f.f.a.$.FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXr 9.f.f.f.k.M FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXT L d.f.n.%.d FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX& > , = W AXlXv 9 B FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX4 u.f.f.7.(.SXhXe FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX@ X.f.f.f.f.f.J P FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX+ +.f.f.f.f.f.f.f.5 FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXo . f.f.f.f.f.f.f.d.X FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFX2 $ q.f.f.f.f.f.f.o.FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFX* p.; | y.f.f.f.i.< FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFX3 f... O } 1 : # FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXO. .% FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFX- FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFX[ ! E E E E E ` XCXDXDXDXDXDXCXaXDXDXDXDXDXDX:XkXmXmXmXmXmX9X_ I I I I I U !.mXmXmXmXmXmX6XFXFX", +"FXFX[ Y I I I I I ) uXVXK 6 VXVXNX*XVXVXVXVXVXVX>XCXDXDXDXDXDXCXaXDXDXDXDXDXDX:XkXmXmXmXmXmX9X_ I I I I I U !.mXmXmXmXmXmX6XFXFX", +"FXFX[ Y I I I I I ) uXVXQ.rXVXVXNX*XVXVXVXVXVXVX>XCXDXDXDXDXDXCXaXDXDXDXDXDXDX:XkXmXmXmXmXmX9X_ I I I I I U !.mXmXmXmXmXmX6XFXFX", +"FXFX[ Y I I I I I ) uXVXVXVXVXVXNX*XVXVXVXVXVXVX>XCXDXDXDXDXDXCXaXDXDXDXDXDXDX:XkXmXmXmXmXmX9X_ I I I I I U !.mXmXmXmXmXmX6XFXFX", +"FXFX[ Y I I I I I ) uXVXVXVXVXVXNX*XVXVXVXVXVXVX>XCXDXDXDXDXDXCXaXDXDXDXDXDXDX:XkXmXmXmXmXmX9X_ I I I I I U !.mXmXmXmXmXmX6XFXFX", +"FXFX{ / ~ ~ ~ ~ ~ ] =XpXpXpXpXpXiX%XpXpXpXpXpXpX&XdXvXvXvXvXvXdX5XvXvXvXvXvXvX@XqXwXwXwXwXwX+X[ ~ ~ ~ ~ ~ ^ ~.wXwXwXwXwXwX#XFXFX", +"FXFXFX2X2X2X2X2X2XoXK.D.D.D.D.D.G.W.,X,X,X,X,X,X[.H.D.D.D.D.D.H.J.D.D.D.D.D.D.P.1X2X2X2X2X2XXXXX2X2X2X2X2X2X/._ _ _ _ _ _ { FXFX", +"FXFXoXbXmXmXmXmXmX8XS.f.f.f.f.f.C.U.VXVXVXVXVXVX>XZ.f.f.f.f.f.Z.F.f.f.f.f.f.f.L.kXmXmXmXmXmX9X6XmXmXmXmXmXnX/.I I I I I I ) FXFX", +"FXFXoXbXmXmXmXmXmX8XS.f.f.f.f.f.C.U.VXVXVXVXVXVX>XZ.f.f.f.f.f.Z.F.f.f.f.f.f.f.L.kXmXmXmXmXmX9X6XmXmXmXmXmXnX/.I I I I I I ) FXFX", +"FXFXoXbXmXmXmXmXmX8XS.f.f.f.f.f.C.U.VXVXVXVXVXVX>XZ.f.f.f.f.f.Z.F.f.f.f.f.f.f.L.kXmXmXmXmXmX9X6XmXmXmXmXmXnX/.I I I I I I ) FXFX", +"FXFXoXbXmXmXmXmXmX8XS.f.f.f.f.f.C.U.VXVXVXVXVXVX>XZ.f.f.f.f.f.Z.F.f.f.f.f.f.f.L.kXmXmXmXmXmX9X6XmXmXmXmXmXnX/.I I I I I I ) FXFX", +"FXFXoXbXmXmXmXmXmX8XS.f.f.f.f.f.C.U.VXVXVXVXVXVX>XZ.f.f.f.f.f.Z.F.f.f.f.f.f.f.L.kXmXmXmXmXmX9X6XmXmXmXmXmXnX/.I I I I I I ) FXFX", +"FXFXoXbXmXmXmXmXmX8XS.f.f.f.f.f.C.U.VXVXVXVXVXVX>XZ.f.f.f.f.f.Z.F.f.f.f.f.f.f.L.kXmXmXmXmXmX9X6XmXmXmXmXmXnX/.I I I I I I ) FXFX", +"FXFXFX^.^.^.^.^.^._.Y.T.T.T.T.T.R.].}.}.}.}.}.}.{.E.T.T.T.T.T.E.T.T.T.T.T.T.T.).^.^.^.^.^.^.~.XXoXoXoXoXoXoX'.^.^.^.^.^.^._.FXFX", +"FXFX[ R Y Y Y Y Y _ 8XxXxXxXxXxXjX XBXBXBXBXBXBX;XeXxXxXxXxXxXeX1XxXxXxXxXxXxX`.Q Y Y Y Y Y ) 3XxXxXxXxXxXzXXXxXxXxXxXxXxX4XFXFX", +"FXFX[ Y I I I I I ) 9XmXmXmXmXmXzXOXVXVXVXVXVXVX>XfXmXmXmXmXmXfX2XmXmXmXmXmXmX_.R I I I I I / 6XmXmXmXmXmXnXXXmXmXmXmXmXmX6XFXFX", +"FXFX[ Y I I I I I ) 9XmXmXmXmXmXzXOXVXVXVXVXVXVX>XfXmXmXmXmXmXfX2XmXmXmXmXmXmX_.R I I I I I / 6XmXmXmXmXmXnXXXmXmXmXmXmXmX6XFXFX", +"FXFX[ Y I I I I I ) 9XmXmXmXmXmXzXOXVXVXVXVXVXVX>XfXmXmXmXmXmXfX2XmXmXmXmXmXmX_.R I I I I I / 6XmXmXmXmXmXnXXXmXmXmXmXmXmX6XFXFX", +"FXFX[ Y I I I I I ) 9XmXmXmXmXmXzXOXVXVXVXVXVXVX>XfXmXmXmXmXmXfX2XmXmXmXmXmXmX_.R I I I I I / 6XmXmXmXmXmXnXXXmXmXmXmXmXmX6XFXFX", +"FXFX[ Y I I I I I ) 9XmXmXmXmXmXzXOXVXVXVXVXVXVX>XfXmXmXmXmXmXfX2XmXmXmXmXmXmX_.R I I I I I / 6XmXmXmXmXmXnXXXmXmXmXmXmXmX6XFXFX", +"FXFX{ ) ( ( ( ( ( { XX7X7X7X7X7X4X|.yXyXyXyXyXyX[.#X7X7X7X7X7X#XXX7X7X7X7X7X7X/._ ( ( ( ( ( { .X7X7X7X7X7X6X'.7X7X7X7X7X7XoXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX", +"FXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFXFX" +}; diff --git a/Plugins/org.mitk.gui.qt.preprocessing.resampling/resources/QmitkPreprocessingResamplingView.qrc b/Plugins/org.mitk.gui.qt.preprocessing.resampling/resources/QmitkPreprocessingResamplingView.qrc new file mode 100644 index 0000000000..d8074b5c48 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.preprocessing.resampling/resources/QmitkPreprocessingResamplingView.qrc @@ -0,0 +1,5 @@ + + + lena.xpm + + diff --git a/Plugins/org.mitk.gui.qt.preprocessing.resampling/resources/lena.xpm b/Plugins/org.mitk.gui.qt.preprocessing.resampling/resources/lena.xpm new file mode 100644 index 0000000000..dbe157cb21 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.preprocessing.resampling/resources/lena.xpm @@ -0,0 +1,391 @@ +/* XPM */ +static char * pixmap[] = { +/* width height num_colors chars_per_pixel */ +"48 48 336 4", +/* colors */ +" c #FDFDFD", +"! c #FCFCFC", +"# c #FBFBFB", +"$ c #F5F5F5", +"% c #F3F3F3", +"& c #EFEFEF", +"' c #EEEEEE", +"( c #EBEBEB", +") c #EAEAEA", +"* c #E9E9E9", +"+ c #E6E6E6", +", c #E5E5E5", +"- c #E1E1E1", +". c #DDDDDD", +"/ c #D9D9D9", +"0 c #D1D1D1", +"1 c #D0D0D0", +"2 c #CCCCCC", +"3 c #C7C7C7", +"4 c #C0C0C0", +"5 c #BFBFBF", +"6 c #BBBBBB", +"7 c #BABABA", +"8 c #B8B9B6", +"9 c #B6B6B6", +": c #B5B5B5", +"; c #B4B4B4", +"< c #B2B2B2", +"= c #B1B1B1", +"> c #ABABAB", +"? c #AAAAAA", +"@ c #A9A9A9", +"A c #A8A8A8", +"B c #A7A7A7", +"C c #A6A6A5", +"D c #A5A5A5", +"E c #A5A5A4", +"F c #A3A3A3", +"G c #A0A0A0", +"H c #9FA09F", +"I c #9E9F99", +"J c #9D9D9A", +"K c #9C9C9B", +"L c #9B9B9B", +"M c #999999", +"N c #989898", +"O c #989895", +"P c #979797", +"Q c #979796", +"R c #949494", +"S c #939393", +"T c #929292", +"U c #919191", +"V c #909090", +"W c #8F8F8F", +"X c #8E8E8E", +"Y c #8D8D8D", +"Z c #8C8C8C", +"[ c #8B8B8B", +"] c #8A8A8A", +"^ c #888888", +"_ c #878787", +"` c #868686", +"a c #858585", +"b c #848484", +"c c #838383", +"d c #828282", +"e c #818181", +"f c #7F7F7F", +"g c #7E7F7E", +"h c #7E7E7E", +"i c #7E7E7D", +"j c #7C7D7A", +"k c #7C7C7C", +"l c #7B7B7B", +"m c #7B7B7A", +"n c #7A7C73", +"o c #7A7A7A", +"p c #797A7A", +"q c #797979", +"r c #787979", +"s c #787878", +"t c #777875", +"u c #777777", +"v c #767777", +"w c #767770", +"x c #76776F", +"y c #767676", +"z c #767675", +"{ c #747474", +"| c #747473", +"} c #737373", +"~ c #737370", +" ! c #727371", +"!! c #727272", +"#! c #71736A", +"$! c #71726A", +"%! c #717171", +"&! c #707170", +"'! c #707070", +"(! c #70706E", +")! c #6F6F6F", +"*! c #6F6F6E", +"+! c #6E6F6E", +",! c #6E6E6E", +"-! c #6E6E6C", +".! c #6D6D6D", +"/! c #6C6E64", +"0! c #6C6C6C", +"1! c #6B6B6B", +"2! c #6A6A6A", +"3! c #696A6A", +"4! c #696969", +"5! c #696867", +"6! c #686968", +"7! c #686868", +"8! c #676767", +"9! c #676766", +":! c #666764", +";! c #666666", +"! c #646464", +"?! c #646463", +"@! c #63655C", +"A! c #636462", +"B! c #636363", +"C! c #636362", +"D! c #626262", +"E! c #626261", +"F! c #616161", +"G! c #616160", +"H! c #606160", +"I! c #606060", +"J! c #60605F", +"K! c #5F605F", +"L! c #5F5F5F", +"M! c #5E5F5E", +"N! c #5E5E5E", +"O! c #5D5D5E", +"P! c #5D5D5D", +"Q! c #5C5C5C", +"R! c #5B5B5B", +"S! c #5A5A5A", +"T! c #5A5A59", +"U! c #595959", +"V! c #595958", +"W! c #59585C", +"X! c #585858", +"Y! c #575758", +"Z! c #575757", +"[! c #565753", +"]! c #565656", +"^! c #565655", +"_! c #555556", +"`! c #555555", +"a! c #545454", +"b! c #545453", +"c! c #535353", +"d! c #535352", +"e! c #525252", +"f! c #515152", +"g! c #515151", +"h! c #505050", +"i! c #4F4F4F", +"j! c #4E4E4E", +"k! c #4D4D4D", +"l! c #4D4D4C", +"m! c #4C4C4C", +"n! c #4B4B4B", +"o! c #4A4B43", +"p! c #4A4A4A", +"q! c #4A4A49", +"r! c #494B42", +"s! c #494949", +"t! c #484A41", +"u! c #484848", +"v! c #474748", +"w! c #474747", +"x! c #46473F", +"y! c #464646", +"z! c #45473F", +"{! c #454545", +"|! c #444444", +"}! c #444443", +"~! c #43443C", +" # c #434343", +"!# c #42443B", +"## c #42443A", +"$# c #424242", +"%# c #424241", +"&# c #414242", +"'# c #414141", +"(# c #414140", +")# c #404040", +"*# c #3F3F3F", +"+# c #3F3F3E", +",# c #3E3E3E", +"-# c #3E3E3D", +".# c #3D3F37", +"/# c #3D3E37", +"0# c #3D3D3D", +"1# c #3D3D3A", +"2# c #3C3C3C", +"3# c #3B3C3B", +"4# c #3B3B3B", +"5# c #393939", +"6# c #393938", +"7# c #35372F", +"8# c #353535", +"9# c #34352D", +":# c #343434", +";# c #33352C", +"<# c #333433", +"=# c #333331", +"># c #32332A", +"?# c #323232", +"@# c #313131", +"A# c #303228", +"B# c #303129", +"C# c #303030", +"D# c #2F3030", +"E# c #2F302E", +"F# c #2F3028", +"G# c #2F3027", +"H# c #2F3021", +"I# c #2F2F2F", +"J# c #2F2F2E", +"K# c #2F2E2D", +"L# c #2E3027", +"M# c #2E2F27", +"N# c #2E2E2E", +"O# c #2D2E2D", +"P# c #2D2E26", +"Q# c #2D2D2D", +"R# c #2C2E25", +"S# c #2C2D29", +"T# c #2C2C2C", +"U# c #2C2C2B", +"V# c #2B2C24", +"W# c #2B2B2C", +"X# c #2A2A2B", +"Y# c #292B22", +"Z# c #292A29", +"[# c #292A21", +"]# c #282827", +"^# c #272920", +"_# c #272726", +"`# c #262820", +"a# c #262627", +"b# c #262626", +"c# c #262625", +"d# c #25271F", +"e# c #252525", +"f# c #242524", +"g# c #24251E", +"h# c #242424", +"i# c #242423", +"j# c #23251D", +"k# c #23241C", +"l# c #23241B", +"m# c #232323", +"n# c #232322", +"o# c #222222", +"p# c #21231A", +"q# c #21221A", +"r# c #212219", +"s# c #202219", +"t# c #20211A", +"u# c #202119", +"v# c #202118", +"w# c #202020", +"x# c #202018", +"y# c #201F18", +"z# c #1F2119", +"{# c #1F2118", +"|# c #1F2020", +"}# c #1F201E", +"~# c #1F2018", +" $ c #1F2017", +"!$ c #1F1F1F", +"#$ c #1F1F1E", +"$$ c #1F1F18", +"%$ c #1E2017", +"&$ c #1E1E1E", +"'$ c #1E1E1D", +"($ c #1D1E16", +")$ c #1D1D1C", +"*$ c #1C1E16", +"+$ c #1C1C1D", +",$ c #1C1C1C", +"-$ c #1C1B1A", +".$ c #1B1B1B", +"/$ c #1B1B1A", +"0$ c #1A1A1A", +"1$ c #191919", +"2$ c #191819", +"3$ c #181918", +"4$ c #181818", +"5$ c #171717", +"6$ c #171714", +"7$ c #161616", +"8$ c #161615", +"9$ c #161514", +":$ c #151515", +";$ c #141514", +"<$ c #141415", +"=$ c #141413", +">$ c #141313", +"?$ c #131313", +"@$ c #131312", +"A$ c #121213", +"B$ c #121212", +"C$ c #121211", +"D$ c #111111", +"E$ c #111110", +"F$ c #101110", +"G$ c #101010", +"H$ c #0F0F0F", +"I$ c #0E0F0E", +"J$ c #0E0E0F", +"K$ c #0E0E0E", +"L$ c #0E0E0D", +"M$ c #0E0D0E", +"N$ c #0E0D0D", +"O$ c #0E0D0C", +"P$ c #0D0D0E", +"Q$ c #0D0D0D", +"R$ c #0D0D0C", +"S$ c #0D0C0C", +"T$ c #0D0C0B", +"U$ c #0C0C0D", +"V$ c #0C0C0C", +"W$ c #0C0C0B", +"X$ c #0B0B0B", +"Y$ c #0B0B0A", +/* pixels */ +"g# g# g# g# g# g# S# S# S# S# S# S# [! [! [! [! [! [! j j j j j j t t O 8 J I w x! /# o! .# P# j# z# k# `# P# t# ($ *$ ($ $$ y# H# ", +"g# g# g# g# g# g# S# S# S# S# S# S# [! [! [! [! [! [! j j j j j j t t A! K P! V! 2! v N 3! h v :# .$ 4$ ?$ 2$ .$ K$ Q$ N$ M$ O$ $$ ", +"g# g# g# g# g# g# S# S# S# S# S# S# [! [! [! [! [! [! j j j j j j t t q! =! W! f! _! X! p S! ;! h : I! 5$ |# h# 0$ ;$ K$ R$ S$ Y$ z# ", +"g# g# g# g# g# g# S# S# S# S# S# S# [! [! [! [! [! [! j j j j j j t t m! |! y! a! e! `! %! B! c! F! D! %! e T# 0$ .$ w# D$ I$ P$ L$ r# ", +"g# g# g# g# g# g# S# S# S# S# S# S# [! [! [! [! [! [! j j j j j j t t g! )# w! P! =! c! 2! 8! k! Q! `! 1! 0! 4! w# &$ ,$ e# L$ K$ S$ v# ", +"g# g# g# g# g# g# S# S# S# S# S# S# [! [! [! [! [! [! j j j j j j t t s! $# '# p! n! s! L! B! k! Z! k! Z! D! [ b b# 0$ 5$ 3$ Q$ N$ ~# ", +"1# 1# 1# 1# 1# 1# =! =! =! =! =! =! h! h! h! h! h! h! I! I! I! I! I! I! S! S! D! w! )# # {! ]! =! e! p! Z! m! ]! ;! P! %! y! h# .$ )$ D$ T$ v# ", +"1# 1# 1# 1# 1# 1# =! =! =! =! =! =! h! h! h! h! h! h! I! I! I! I! I! I! S! S! .! n! ,# ,# w! F! g! p! y! # w! # y! e! S! U! m# .$ /$ +$ N$ $ ", +"1# 1# 1# 1# 1# 1# =! =! =! =! =! =! h! h! h! h! h! h! I! I! I! I! I! I! S! S! N! u! )# '# y! N! ]! n! n! w! |! n! R! 4! 0! ` ,# b# ]# .$ >$ q# ", +"1# 1# 1# 1# 1# 1# =! =! =! =! =! =! h! h! h! h! h! h! I! I! I! I! I! I! S! S! S! u! $# $# |! n! u! '# # y! u! X! .! =! F! e w# o# i# a# 9$ {# ", +"1# 1# 1# 1# 1# 1# =! =! =! =! =! =! h! h! h! h! h! h! I! I! I! I! I! I! S! S! k! *# # $# # |! y! p! 2! !! N! i! 2! 7! )! R! 0$ 1$ O# 8# #$ l# ", +"1# 1# 1# 1# 1# 1# =! =! =! =! =! =! h! h! h! h! h! h! I! I! I! I! I! I! S! S! y! {! g! m! *# )# {! U! =! ,! u l } u !! $# 4$ &$ U# 4# _# V# ", +":! :! :! :! :! :! L L L L L L l l l l l l h h h h h h L! L! {! a! l j! ,# y! n! R! D! N! F! .! ' L U! j! u! 4$ E# *# f# F# ", +":! :! :! :! :! :! L L L L L L l l l l l l h h h h h h L! L! X! 4! 2! Q! n! p! p! n! U! S! L! F , # ( > X! I# Z# C# +# ># ", +":! :! :! :! :! :! L L L L L L l l l l l l h h h h h h L! L! d =! I! S! ]! R! i! m! e! =! _ 1 Z * / + % C# '$ D# c# ;# ", +":! :! :! :! :! :! L L L L L L l l l l l l h h h h h h L! L! ,! I! a! i! N! P! R! j! S! 2! %! ,! %! W ? 4 - ; 7$ 0$ J# z! ", +":! :! :! :! :! :! L L L L L L l l l l l l h h h h h h L! L! e h! ,# i! N! N! Q! R! a! P! 0! l ,! s y { T 5 -# A$ #$ ~! ", +":! :! :! :! :! :! L L L L L L l l l l l l h h h h h h L! L! > {! )# $# c! D! X! g! g! X! 1! %! )! U U )! 7! D H <$ -$ F# ", +"(! (! (! (! (! (! 5 5 5 5 5 5 l l l l l l ,! ,! ,! ,! ,! ,! R! R! P i! # 2# {! k! g! j! e! a! I! 1! a 7 Z y o A C F! C$ B# ", +"(! (! (! (! (! (! 5 5 5 5 5 5 l l l l l l ,! ,! ,! ,! ,! ,! R! R! >! w! # {! ,# w! g! Q! n! Z! I! ,! B 0 B V k Y E ^ }# G# ", +"(! (! (! (! (! (! 5 5 5 5 5 5 l l l l l l ,! ,! ,! ,! ,! ,! R! R! j! p! w! s! $# 0# m! {! s! Z! D! } P + U 2! )! S! ! e! h! k! l! h! ^! x ", +"-! -! -! -! -! -! 9 9 9 9 9 9 d d d d d d F! F! F! F! F! F! I! I! } D! P! Q! c! g! m! k! k! X! L! a! c! F! c! s! j! c! M! !! 5! /! ", +"-! -! -! -! -! -! 9 9 9 9 9 9 d d d d d d F! F! F! F! F! F! I! I! D 0! Z! Q! ]! e! i! n! n! h! h! g! ]! e! c! c! e! R! +! a Q @! ", +"-! -! -! -! -! -! 9 9 9 9 9 9 d d d d d d F! F! F! F! F! F! I! I! h f u L! Q! c! c! m! |! s! m! ]! c! i! c! `! Q! L! E! B! G! n ", +"-! -! -! -! -! -! 9 9 9 9 9 9 d d d d d d F! F! F! F! F! F! I! I! X! a! k! '! b ] P .! j! s! h! `! n! h! c! Z! =! 4! H! N! ! = & ) $ F p! m! m! m! S! 7! 1! ` c &! >! 9! r! ", +"~ ~ ~ ~ ~ ~ @ @ @ @ @ @ X X X X X X u u u u u u =! =! h! c! U! S! ] ( # 3 h! a! e! e! 2! %! .! 7! 4! C! >! *! A# ", +"~ ~ ~ ~ ~ ~ @ @ @ @ @ @ X X X X X X u u u u u u =! =! ;! S! h! Z! R! R . ! 2 c! g! n! `! P! >! 4! )! { m e b! L# ", +"~ ~ ~ ~ ~ ~ @ @ @ @ @ @ X X X X X X u u u u u u =! =! '! c! i! ]! a! N! y 6 < c! g! i! g! c! `! ]! ]! 2! g T }! [# ", +"~ ~ ~ ~ ~ ~ @ @ @ @ @ @ X X X X X X u u u u u u =! =! 8! P! h! `! a! a! ]! N! o a! e! e! F! B! Q! `! j! F! 6! =! K# M# ", +"~ ~ ~ ~ ~ ~ @ @ @ @ @ @ X X X X X X u u u u u u =! =! S! R! S! ,! y U! U! c! S! g! g! c! R! { } 2! P! P! C! k! /$ R# ", +"=# =# =# =# =# =# [ [ [ [ [ [ !! !! !! !! !! !! X! X! X! X! X! X! o o } M a = '! c! `! Q! P! `! g! n! u! Z! >! 1! Z e ! # E$ d# ", +"=# =# =# =# =# =# [ [ [ [ [ [ !! !! !! !! !! !! X! X! X! X! X! X! o o ^ '! U! V >! a! U! S! Z! c! e! h! j! k! j! `! L! 8! ?! !$ =$ 7# ", +"=# =# =# =# =# =# [ [ [ [ [ [ !! !! !! !! !! !! X! X! X! X! X! X! o o .! 1! j! g! ]! `! X! Z! g! c! c! R! D! I! X! P! =! 7! d! B$ ;$ ## ", +"=# =# =# =# =# =# [ [ [ [ [ [ !! !! !! !! !! !! X! X! X! X! X! X! o o S! g! i! g! `! U! Q! k! S! Q! S! S! B! 4! `! I! 7! s @$ G$ (# !# ", +"=# =# =# =# =# =# [ [ [ [ [ [ !! !! !! !! !! !! X! X! X! X! X! X! o o N! ]! a! `! `! Z! R! g! ]! !! q 2! P! { S! 8! M Q# F$ &$ <# Y# ", +"=# =# =# =# =# =# [ [ [ [ [ [ !! !! !! !! !! !! X! X! X! X! X! X! o o 1! >! g! >! S! X! `! a! R! 2! q )! 7! y R! P! {! 1$ n# 5# i# x# ", +"6$ 6$ 6$ 6$ 6$ 6$ X# X# X# X# X# X# p! p! p! p! p! p! J! J! J! J! J! J! } } D! >! D! )! 2! L! N! g! Z! 4! B! q d e %! ]! ?$ 4$ 3# &# 8$ u# ", +"6$ 6$ 6$ 6$ 6$ 6$ X# X# X# X# X# X# p! p! p! p! p! p! J! J! J! J! J! J! } } P! !! } X! U! R! a! e! ]! 1! u %! B 7 m! 4$ 7$ N# 6# 0$ L$ v# ", +"6$ 6$ 6$ 6$ 6$ 6$ X# X# X# X# X# X# p! p! p! p! p! p! J! J! J! J! J! J! } } F! c! u ]! Z! c! `! k! c! >! '! 4! 0! w! :$ :$ h# $# '$ H$ N$ %$ ", +"6$ 6$ 6$ 6$ 6$ 6$ X# X# X# X# X# X# p! p! p! p! p! p! J! J! J! J! J! J! } } Z! `! g! Z! S! h! u L! e! c! Z! R! C# D$ 7$ @# ?# :$ X$ Q$ W$ s# ", +"6$ 6$ 6$ 6$ 6$ 6$ X# X# X# X# X# X# p! p! p! p! p! p! J! J! J! J! J! J! } } N! ]! w! I! P! i! 4! a S! D! w! 4$ :$ ?$ T# I# G$ V$ W$ V$ W$ p# ", +"6$ 6$ 6$ 6$ 6$ 6$ X# X# X# X# X# X# p! p! p! p! p! p! J! J! J! J! J! J! } } v! Y! Z! G Z! f! c! >! O! W# J$ .$ e# a# &$ D$ K$ H$ Q$ U$ R$ u# " +}; diff --git a/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/QmitkPreprocessingResamplingView.cpp b/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/QmitkPreprocessingResamplingView.cpp new file mode 100644 index 0000000000..47556da11c --- /dev/null +++ b/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/QmitkPreprocessingResamplingView.cpp @@ -0,0 +1,460 @@ +/*=================================================================== + +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 "QmitkPreprocessingResamplingView.h" + +// QT includes (GUI) +#include +#include +#include +#include +#include +#include +#include + +// Berry includes (selection service) +#include +#include + +// MITK includes (GUI) +#include "QmitkStdMultiWidget.h" +#include "QmitkDataNodeSelectionProvider.h" +#include "mitkDataNodeObject.h" + +// MITK includes (general) +#include "mitkNodePredicateDataType.h" +#include "mitkNodePredicateDimension.h" +#include "mitkNodePredicateAnd.h" +#include "mitkImageTimeSelector.h" +#include "mitkVectorImageMapper2D.h" +#include "mitkProperties.h" + +// Includes for image casting between ITK and MITK +#include "mitkImageCast.h" +#include "mitkITKImageImport.h" + +// ITK includes (general) +#include +#include + +// Resampling +#include +#include +#include +#include +#include + +#include +#include + +// STD +#include + + +// Convenient Definitions +typedef itk::Image ImageType; +typedef itk::Image SegmentationImageType; +typedef itk::Image DoubleImageType; +typedef itk::Image, 3> VectorImageType; + +typedef itk::ResampleImageFilter< ImageType, ImageType > ResampleImageFilterType; +typedef itk::ResampleImageFilter< ImageType, ImageType > ResampleImageFilterType2; +typedef itk::CastImageFilter< ImageType, DoubleImageType > ImagePTypeToFloatPTypeCasterType; + +typedef itk::LinearInterpolateImageFunction< ImageType, double > LinearInterpolatorType; +typedef itk::NearestNeighborInterpolateImageFunction< ImageType, double > NearestInterpolatorType; +typedef itk::BSplineInterpolateImageFunction BSplineInterpolatorType; + + +QmitkPreprocessingResampling::QmitkPreprocessingResampling() +: QmitkAbstractView(), + m_Controls(NULL), + m_SelectedImageNode(NULL), + m_TimeStepperAdapter(NULL) +{ +} + +QmitkPreprocessingResampling::~QmitkPreprocessingResampling() +{ +} + +void QmitkPreprocessingResampling::CreateQtPartControl(QWidget *parent) +{ + if (m_Controls == NULL) + { + m_Controls = new Ui::QmitkPreprocessingResamplingViewControls; + m_Controls->setupUi(parent); + this->CreateConnections(); + + mitk::NodePredicateDimension::Pointer dimensionPredicate = mitk::NodePredicateDimension::New(3); + mitk::NodePredicateDataType::Pointer imagePredicate = mitk::NodePredicateDataType::New("Image"); + } + + m_SelectedImageNode = mitk::DataStorageSelection::New(this->GetDataStorage(), false); + + // Setup Controls + this->m_Controls->cbParam4->clear(); + this->m_Controls->cbParam4->insertItem(LINEAR, "Linear"); + this->m_Controls->cbParam4->insertItem(NEAREST, "Nearest neighbor"); + this->m_Controls->cbParam4->insertItem(SPLINE, "B-Spline"); + +} + +void QmitkPreprocessingResampling::CreateConnections() +{ + if ( m_Controls ) + { + connect((QObject*)(m_Controls->btnDoIt), SIGNAL(clicked()), (QObject*) this, SLOT(StartButtonClicked())); + connect((QObject*)(m_Controls->buttonExecuteOnMultipleImages), SIGNAL(clicked()), (QObject*) this, SLOT(StartMultipleImagesButtonClicked())); + connect( (QObject*)(m_Controls->cbParam4), SIGNAL( activated(int) ), this, SLOT( SelectInterpolator(int) ) ); + } +} + +void QmitkPreprocessingResampling::InternalGetTimeNavigationController() +{ + auto renwin_part = GetRenderWindowPart(); + if( renwin_part != nullptr ) + { + auto tnc = renwin_part->GetTimeNavigationController(); + if( tnc != nullptr ) + { + m_TimeStepperAdapter = new QmitkStepperAdapter((QObject*) m_Controls->sliceNavigatorTime, tnc->GetTime(), "sliceNavigatorTimeFromBIP"); + } + } +} + +void QmitkPreprocessingResampling::SetFocus() +{ +} + +//datamanager selection changed +void QmitkPreprocessingResampling::OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList& nodes) +{ + ResetOneImageOpPanel(); + //any nodes there? + if (!nodes.empty()) + { + // reset GUI + m_Controls->sliceNavigatorTime->setEnabled(false); + m_Controls->leImage1->setText(tr("Select an Image in Data Manager")); + + m_SelectedNodes.clear(); + + for (mitk::DataNode* _DataNode : nodes) + { + m_SelectedImageNode->RemoveAllNodes(); + *m_SelectedImageNode = _DataNode; + mitk::Image::Pointer tempImage = dynamic_cast(m_SelectedImageNode->GetNode()->GetData()); + + //no image + if (tempImage.IsNull() || (tempImage->IsInitialized() == false)) + { + if (m_SelectedNodes.size() < 1) + { + m_Controls->leImage1->setText(tr("Not an image.")); + } + continue; + } + + //2D image + if (tempImage->GetDimension() < 3) + { + if (m_SelectedNodes.size() < 1) + { + m_Controls->leImage1->setText(tr("2D images are not supported.")); + } + continue; + } + + if (m_SelectedNodes.size() < 1) + { + m_Controls->leImage1->setText(QString(m_SelectedImageNode->GetNode()->GetName().c_str())); + mitk::Vector3D aSpacing = tempImage->GetGeometry()->GetSpacing(); + std::string text("x-spacing (" + std::to_string(aSpacing[0]) + ")"); + m_Controls->tlParam1->setText(text.c_str()); + text = "y-spacing (" + std::to_string(aSpacing[1]) + ")"; + m_Controls->tlParam2->setText(text.c_str()); + text = "z-spacing (" + std::to_string(aSpacing[2]) + ")"; + m_Controls->tlParam3->setText(text.c_str()); + + if (tempImage->GetDimension() > 3) + { + // try to retrieve the TNC (for 4-D Processing ) + this->InternalGetTimeNavigationController(); + + m_Controls->sliceNavigatorTime->setEnabled(true); + m_Controls->tlTime->setEnabled(true); + } + } + m_SelectedNodes.push_back(_DataNode); + } + if (m_SelectedNodes.size() > 0) + { + *m_SelectedImageNode = m_SelectedNodes[0]; + } + ResetParameterPanel(); + } +} + +void QmitkPreprocessingResampling::ResetOneImageOpPanel() +{ + m_Controls->tlTime->setEnabled(false); + m_Controls->btnDoIt->setEnabled(false); + m_Controls->buttonExecuteOnMultipleImages->setEnabled(false); + m_Controls->cbHideOrig->setEnabled(false); + m_Controls->leImage1->setText(tr("Select an Image in Data Manager")); + m_Controls->tlParam1->setText("x-spacing"); + m_Controls->tlParam1->setText("y-spacing"); + m_Controls->tlParam1->setText("z-spacing"); +} + +void QmitkPreprocessingResampling::ResetParameterPanel() +{ + m_Controls->btnDoIt->setEnabled(true); + m_Controls->buttonExecuteOnMultipleImages->setEnabled(true); + m_Controls->cbHideOrig->setEnabled(true); +} + +void QmitkPreprocessingResampling::ResetTwoImageOpPanel() +{ +} + +void QmitkPreprocessingResampling::StartMultipleImagesButtonClicked() +{ + for (auto currentSelectedNode : m_SelectedNodes) + { + m_SelectedImageNode->RemoveAllNodes(); + *m_SelectedImageNode = currentSelectedNode; + StartButtonClicked(); + } +} + +void QmitkPreprocessingResampling::StartButtonClicked() +{ + if(!m_SelectedImageNode->GetNode()) return; + + this->BusyCursorOn(); + + mitk::Image::Pointer newImage; + + try + { + newImage = dynamic_cast(m_SelectedImageNode->GetNode()->GetData()); + } + catch ( std::exception &e ) + { + QString exceptionString = tr("An error occured during image loading:\n"); + exceptionString.append( e.what() ); + QMessageBox::warning( NULL, "Preprocessing - Resampling: ", exceptionString , QMessageBox::Ok, QMessageBox::NoButton ); + this->BusyCursorOff(); + return; + } + + // check if input image is valid, casting does not throw exception when casting from 'NULL-Object' + if ( (! newImage) || (newImage->IsInitialized() == false) ) + { + this->BusyCursorOff(); + + QMessageBox::warning( NULL, "Preprocessing - Resampling", tr("Input image is broken or not initialized. Returning."), QMessageBox::Ok, QMessageBox::NoButton ); + return; + } + + // check if operation is done on 4D a image time step + if(newImage->GetDimension() > 3) + { + mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); + timeSelector->SetInput(newImage); + timeSelector->SetTimeNr( ((QmitkSliderNavigatorWidget*)m_Controls->sliceNavigatorTime)->GetPos() ); + timeSelector->Update(); + newImage = timeSelector->GetOutput(); + } + + // check if image or vector image + ImageType::Pointer itkImage = ImageType::New(); + VectorImageType::Pointer itkVecImage = VectorImageType::New(); + + int isVectorImage = newImage->GetPixelType().GetNumberOfComponents(); + + if(isVectorImage > 1) + { + CastToItkImage( newImage, itkVecImage ); + } + else + { + CastToItkImage( newImage, itkImage ); + } + + std::stringstream nameAddition(""); + + double dparam1 = m_Controls->dsbParam1->value(); + double dparam2 = m_Controls->dsbParam2->value(); + double dparam3 = m_Controls->dsbParam3->value(); + + try{ + + std::string selectedInterpolator; + ResampleImageFilterType::Pointer resampler = ResampleImageFilterType::New(); + switch (m_SelectedInterpolation) + { + case LINEAR: + { + LinearInterpolatorType::Pointer interpolator = LinearInterpolatorType::New(); + resampler->SetInterpolator(interpolator); + selectedInterpolator = "Linear"; + break; + } + case NEAREST: + { + NearestInterpolatorType::Pointer interpolator = NearestInterpolatorType::New(); + resampler->SetInterpolator(interpolator); + selectedInterpolator = "Nearest"; + break; + } + case SPLINE: + { + BSplineInterpolatorType::Pointer interpolator = BSplineInterpolatorType::New(); + interpolator->SetSplineOrder(3); + resampler->SetInterpolator(interpolator); + selectedInterpolator = "B-Spline"; + break; + } + default: + { + LinearInterpolatorType::Pointer interpolator = LinearInterpolatorType::New(); + resampler->SetInterpolator(interpolator); + selectedInterpolator = "Linear"; + break; + } + } + resampler->SetInput( itkImage ); + resampler->SetOutputOrigin( itkImage->GetOrigin() ); + + ImageType::SizeType input_size = itkImage->GetLargestPossibleRegion().GetSize(); + ImageType::SpacingType input_spacing = itkImage->GetSpacing(); + + ImageType::SizeType output_size; + ImageType::SpacingType output_spacing; + + if (dparam1 > 0) + { + output_size[0] = std::ceil(input_size[0] * (input_spacing[0] / dparam1)); + output_spacing[0] = dparam1; + } + else + { + output_size[0] = std::ceil(input_size[0] * (-1.0 / dparam1)); + output_spacing[0] = -1.0*input_spacing[0] * dparam1; + } + if (dparam2 > 0) + { + output_size[1] = std::ceil(input_size[1] * (input_spacing[1] / dparam2)); + output_spacing[1] = dparam2; + } + else + { + output_size[1] = std::ceil(input_size[1] * (-1.0 / dparam2)); + output_spacing[1] = -1.0*input_spacing[1] * dparam2; + } + if (dparam3 > 0) + { + output_size[2] = std::ceil(input_size[2] * (input_spacing[2] / dparam3)); + output_spacing[2] = dparam3; + } + else + { + output_size[2] = std::ceil(input_size[2] * (-1.0 / dparam3)); + output_spacing[2] = -1.0*input_spacing[2] * dparam3; + } + + resampler->SetSize( output_size ); + resampler->SetOutputSpacing( output_spacing ); + resampler->SetOutputDirection( itkImage->GetDirection() ); + + resampler->UpdateLargestPossibleRegion(); + + ImageType::Pointer resampledImage = resampler->GetOutput(); + + newImage = mitk::ImportItkImage( resampledImage )->Clone(); + nameAddition << "_Resampled_" << selectedInterpolator; + std::cout << "Resampling successful." << std::endl; + } + catch (...) + { + this->BusyCursorOff(); + QMessageBox::warning(NULL, "Warning", "Problem when applying filter operation. Check your input..."); + return; + } + + newImage->DisconnectPipeline(); + + // adjust level/window to new image + mitk::LevelWindow levelwindow; + levelwindow.SetAuto( newImage ); + mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); + levWinProp->SetLevelWindow( levelwindow ); + + // compose new image name + std::string name = m_SelectedImageNode->GetNode()->GetName(); + if (name.find(".pic.gz") == name.size() -7 ) + { + name = name.substr(0,name.size() -7); + } + name.append( nameAddition.str() ); + + // create final result MITK data storage node + mitk::DataNode::Pointer result = mitk::DataNode::New(); + result->SetProperty( "levelwindow", levWinProp ); + result->SetProperty( "name", mitk::StringProperty::New( name.c_str() ) ); + result->SetData( newImage ); + + // for vector images, a different mapper is needed + if(isVectorImage > 1) + { + mitk::VectorImageMapper2D::Pointer mapper = + mitk::VectorImageMapper2D::New(); + result->SetMapper(1,mapper); + } + + // add new image to data storage and set as active to ease further processing + GetDataStorage()->Add( result, m_SelectedImageNode->GetNode() ); + if ( m_Controls->cbHideOrig->isChecked() == true ) + m_SelectedImageNode->GetNode()->SetProperty( "visible", mitk::BoolProperty::New(false) ); + + // show the results + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + this->BusyCursorOff(); +} + +void QmitkPreprocessingResampling::SelectInterpolator(int interpolator) +{ + switch (interpolator) + { + case 0: + { + m_SelectedInterpolation = LINEAR; + break; + } + case 1: + { + m_SelectedInterpolation = NEAREST; + break; + } + case 2: + { + m_SelectedInterpolation = SPLINE; + } + } +} diff --git a/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/QmitkPreprocessingResamplingView.h b/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/QmitkPreprocessingResamplingView.h new file mode 100644 index 0000000000..4551f973ef --- /dev/null +++ b/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/QmitkPreprocessingResamplingView.h @@ -0,0 +1,135 @@ +/*=================================================================== + +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. + +===================================================================*/ + +#if !defined(QmitkPreprocessingResamplingView_H__INCLUDED) +#define QmitkPreprocessingResamplingView_H__INCLUDED + +#include +#include +#include "ui_QmitkPreprocessingResamplingViewControls.h" + +#include "QmitkStepperAdapter.h" + +#include + +/*! +\brief This module allows to use some basic image processing filters for preprocessing, image enhancement and testing purposes + +Several basic ITK image processing filters, like denoising, morphological and edge detection +are encapsulated in this module and can be selected via a list and an intuitive parameter input. +The selected filter will be applied on the image, and a new image showing the output is displayed +as result. +Also, some image arithmetic operations are available. + +Images can be 3D or 4D. +In the 4D case, the filters work on the 3D image selected via the +time slider. The result is also a 3D image. + +\sa QmitkFunctionality, QObject + +\class QmitkBasicImageProcessing +\author Tobias Schwarz +\version 1.0 (3M3) +\date 2009-05-10 +\ingroup Bundles +*/ + +class PREPROCESSING_RESAMPLING_EXPORT QmitkPreprocessingResampling : public QmitkAbstractView +{ + Q_OBJECT + +public: + + /*! + \brief default constructor + */ + QmitkPreprocessingResampling(); + + /*! + \brief default destructor + */ + virtual ~QmitkPreprocessingResampling(); + + /*! + \brief method for creating the widget containing the application controls, like sliders, buttons etc. + */ + virtual void CreateQtPartControl(QWidget *parent) override; + + virtual void SetFocus() override; + + /*! + \brief method for creating the connections of main and control widget + */ + virtual void CreateConnections(); + + /*! + \brief Invoked when the DataManager selection changed + */ + virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList& nodes) override; + + + protected slots: + + /* + * The "Execute" button in the "one image ops" box was triggered + */ + void StartButtonClicked(); + + void StartMultipleImagesButtonClicked(); + + void SelectInterpolator(int interpolator); + +private: + + /* + * After a one image operation, reset the "one image ops" panel + */ + void ResetOneImageOpPanel(); + + /* + * Helper method to reset the parameter set panel + */ + void ResetParameterPanel(); + + /* + * After a two image operation, reset the "two image ops" panel + */ + void ResetTwoImageOpPanel(); + + /** retrieve the tnc from renderwindow part */ + void InternalGetTimeNavigationController(); + + /*! + * controls containing sliders for scrolling through the slices + */ + Ui::QmitkPreprocessingResamplingViewControls *m_Controls; + + //mitk::DataNode* m_SelectedImageNode; + mitk::DataStorageSelection::Pointer m_SelectedImageNode; + QmitkStepperAdapter* m_TimeStepperAdapter; + + std::vector m_SelectedNodes; + + enum InterpolationType{ + LINEAR, + NEAREST, + SPLINE + } m_SelectedInterpolation; +}; + +#endif // !defined(QmitkBasicImageProcessing_H__INCLUDED) + + diff --git a/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/QmitkPreprocessingResamplingViewControls.ui b/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/QmitkPreprocessingResamplingViewControls.ui new file mode 100644 index 0000000000..da47359e1b --- /dev/null +++ b/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/QmitkPreprocessingResamplingViewControls.ui @@ -0,0 +1,316 @@ + + + QmitkPreprocessingResamplingViewControls + + + + 0 + 0 + 448 + 980 + + + + Form + + + + + + Resample multiple images + + + + + + + + + + true + + + + 0 + + + 6 + + + 0 + + + 6 + + + + + Select an Image in Data Manager + + + true + + + + + + + false + + + Output image will be 3D + + + Choose time step if 4D +(Slider for both images) + + + false + + + + + + + false + + + Output image will be 3D + + + + + + + + + + Qt::Vertical + + + + 254 + 403 + + + + + + + + Resample single image + + + + + + + + + + true + + + + 0 + + + 6 + + + 0 + + + 0 + + + + + true + + + + 0 + 0 + + + + Qt::LeftToRight + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 5 + + + -100.000000000000000 + + + 100.000000000000000 + + + 1.000000000000000 + + + + + + + true + + + + 0 + 0 + + + + 5 + + + -100.000000000000000 + + + 100.000000000000000 + + + 1.000000000000000 + + + + + + + true + + + + 0 + 0 + + + + 5 + + + -100.000000000000000 + + + 100.000000000000000 + + + 1.000000000000000 + + + + + + + false + + + Resampling Parameter + + + false + + + + + + + false + + + x-spacing + + + false + + + + + + + false + + + y-spacing + + + false + + + + + + + false + + + Hide Original Image + + + true + + + + + + + false + + + z-spacing + + + + + + + false + + + Interpolation: + + + + + + + true + + + + 0 + 0 + + + + + + + + + + + + QmitkSliderNavigatorWidget + QWidget +
QmitkSliderNavigatorWidget.h
+
+
+ + dsbParam1 + dsbParam2 + dsbParam3 + cbParam4 + cbHideOrig + btnDoIt + buttonExecuteOnMultipleImages + leImage1 + + + +
diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.cpp b/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/mitkPreprocessingResamplingActivator.cpp similarity index 60% copy from Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.cpp copy to Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/mitkPreprocessingResamplingActivator.cpp index 3fb6fb829a..3f1fe33bef 100644 --- a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.cpp +++ b/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/mitkPreprocessingResamplingActivator.cpp @@ -1,33 +1,32 @@ /*=================================================================== 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 "org_mitk_gui_qt_classificationsegmentation_Activator.h" -#include "ClassificationSegmentation.h" +#include "mitkPreprocessingResamplingActivator.h" +#include "QmitkPreprocessingResamplingView.h" namespace mitk { -void org_mitk_gui_qt_classificationsegmentation_Activator::start(ctkPluginContext* context) +void PreprocessingResamplingActivator::start(ctkPluginContext* context) { - BERRY_REGISTER_EXTENSION_CLASS(ClassificationSegmentation, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkPreprocessingResampling, context) } -void org_mitk_gui_qt_classificationsegmentation_Activator::stop(ctkPluginContext* context) +void PreprocessingResamplingActivator::stop(ctkPluginContext* context) { Q_UNUSED(context) } } diff --git a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.h b/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/mitkPreprocessingResamplingActivator.h similarity index 58% copy from Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.h copy to Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/mitkPreprocessingResamplingActivator.h index 02fc27aa6f..c757c5502c 100644 --- a/Plugins/org.mitk.gui.qt.classificationsegmentation/src/internal/org_mitk_gui_qt_classificationsegmentation_Activator.h +++ b/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/mitkPreprocessingResamplingActivator.h @@ -1,41 +1,40 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ - -#ifndef org_mitk_gui_qt_classificationsegmentation_Activator_h -#define org_mitk_gui_qt_classificationsegmentation_Activator_h +#ifndef MITKBPREPROCESSINGRESAMPLINGACTIVATOR_H +#define MITKBPREPROCESSINGRESAMPLINGACTIVATOR_H #include namespace mitk { -class org_mitk_gui_qt_classificationsegmentation_Activator : +class PreprocessingResamplingActivator : public QObject, public ctkPluginActivator { Q_OBJECT - Q_PLUGIN_METADATA(IID "org_mitk_gui_qt_classificationsegmentation") + Q_PLUGIN_METADATA(IID "org_mitk_gui_qt_preprocessing_resampling") Q_INTERFACES(ctkPluginActivator) public: - void start(ctkPluginContext* context); - void stop(ctkPluginContext* context); + void start(ctkPluginContext* context) override; + void stop(ctkPluginContext* context) override; -}; // org_mitk_gui_qt_classificationsegmentation_Activator +}; // basicImageProcessingActivator } -#endif // org_mitk_gui_qt_classificationsegmentation_Activator_h +#endif // MITKBPREPROCESSINGRESAMPLINGACTIVATOR_H diff --git a/Wrapper/swigutils.cmake b/Wrapper/swigutils.cmake new file mode 100644 index 0000000000..516778aff8 --- /dev/null +++ b/Wrapper/swigutils.cmake @@ -0,0 +1,63 @@ +macro(setup_swig) + if (NOT SWIG_DIR) + set(SWIG_DIR $ENV{SWIG_DIR} ) + endif() + if (NOT SWIG_EXECUTABLE) + set(SWIG_EXECUTABLE $ENV{SWIG_EXECUTABLE} ) + endif() + find_package(SWIG REQUIRED) + INCLUDE(${SWIG_USE_FILE}) + message(STATUS "SWIG_DIR = ${SWIG_DIR}" ) + message(STATUS "SWIG_EXECUTABLE = ${SWIG_EXECUTABLE}" ) +endmacro() + +macro(setup_python) + # CMake's python-detection is crazy broken, so we use python-config on *nix. + # Sadly on Windows python-config isn't available, so do our best with CMake. + # + # see https://cmake.org/Bug/view.php?id=14809 + # + if (WIN32) + find_package(PythonInterp) + find_package(PythonLibs) + + set(PYTHON_CFLAGS "-I${PYTHON_INCLUDE_PATH}") + set(PYTHON_LDFLAGS "${PYTHON_LIBRARIES}") + else() + if (ARCH STREQUAL "arm") + find_program(PYTHON_CONFIG_EXECUTABLE arm-linux-gnueabihf-python-config) + else() + find_program(PYTHON_CONFIG_EXECUTABLE python-config) + endif() + if (PYTHON_CONFIG_EXECUTABLE) + execute_process(COMMAND ${PYTHON_CONFIG_EXECUTABLE} --cflags OUTPUT_VARIABLE PYTHON_CFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${PYTHON_CONFIG_EXECUTABLE} --ldflags OUTPUT_VARIABLE PYTHON_LDFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + if (NOT PYTHON_EXECUTABLE) + execute_process(COMMAND ${PYTHON_CONFIG_EXECUTABLE} --exec-prefix OUTPUT_VARIABLE PYTHON_EXECUTABLE OUTPUT_STRIP_TRAILING_WHITESPACE) + set(PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE}/bin/python) + endif() + endif() + endif() + + if (PYTHON_EXECUTABLE) + # get the python version + execute_process(COMMAND ${PYTHON_EXECUTABLE} --version ERROR_VARIABLE PYTHON_VERSION) + # message("rv='${PYTHON_VERSION}'") + string(STRIP ${PYTHON_VERSION} PYTHON_VERSION) + string(REPLACE " " ";" PYTHON_VERSION ${PYTHON_VERSION}) + list(GET PYTHON_VERSION 1 PYTHON_VERSION) + + message(STATUS "PYTHON_EXECUTABLE= ${PYTHON_EXECUTABLE}") + message(STATUS "PYTHON_VERSION= ${PYTHON_VERSION}") + message(STATUS "PYTHON_CFLAGS= ${PYTHON_CFLAGS}") + message(STATUS "PYTHON_LDFLAGS= ${PYTHON_LDFLAGS}") + + set(PYTHON 1) + endif() +endmacro() + +# build-time file replacement +set(FILE_REPLACE_SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/replace.cmake) +file(WRITE ${FILE_REPLACE_SCRIPT} "file(READ \${FROM} file_contents) \n") +file(APPEND ${FILE_REPLACE_SCRIPT} "string(REPLACE \${MATCH_STRING} \${REPLACE_STRING} file_contents \${file_contents}) \n") +file(APPEND ${FILE_REPLACE_SCRIPT} "file(WRITE \${TO} \${file_contents}) \n")