diff --git a/Data/Baseline/OTB/Images/apTvFEPantexTextureExtraction.tif b/Data/Baseline/OTB/Images/apTvFEPantexTextureExtraction.tif new file mode 100644 index 0000000000000000000000000000000000000000..0f06580c8a75bbd923681514926c20a63f0f60eb --- /dev/null +++ b/Data/Baseline/OTB/Images/apTvFEPantexTextureExtraction.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2236e9d397c3608bca1de0353d812a609153ac33c45c7f761919167edf8398a1 +size 212424 diff --git a/Modules/Applications/AppTextures/app/CMakeLists.txt b/Modules/Applications/AppTextures/app/CMakeLists.txt index dce1e5a1341c3863c10b01f9e798570d438df897..35119ec6521ec7a4469cb3d15b89e0950862c9ea 100644 --- a/Modules/Applications/AppTextures/app/CMakeLists.txt +++ b/Modules/Applications/AppTextures/app/CMakeLists.txt @@ -27,3 +27,8 @@ otb_create_application( NAME SFSTextureExtraction SOURCES otbSFSTextureExtraction.cxx LINK_LIBRARIES ${${otb-module}_LIBRARIES}) + +otb_create_application( + NAME PantexTextureExtraction + SOURCES otbPantexTextureExtraction.cxx + LINK_LIBRARIES ${${otb-module}_LIBRARIES}) diff --git a/Modules/Applications/AppTextures/app/otbPantexTextureExtraction.cxx b/Modules/Applications/AppTextures/app/otbPantexTextureExtraction.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5655c7bd5ec6c357334c1a1b9b0914703f105408 --- /dev/null +++ b/Modules/Applications/AppTextures/app/otbPantexTextureExtraction.cxx @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * 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 + * + * 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. + */ + +#include "otbWrapperApplication.h" +#include "otbWrapperApplicationFactory.h" + +#include "otbMultiToMonoChannelExtractROI.h" +#include "otbScalarImageToPanTexTextureFilter.h" +#include "otbStreamingMinMaxImageFilter.h" + +namespace otb +{ +namespace Wrapper +{ + +class PantexTextureExtraction : public Application +{ +public: + + /** @name Standard class typedefs + * @{ + */ + using Self = PantexTextureExtraction; + using Superclass = Application; + using Pointer = itk::SmartPointer<Self>; + using ConstPointer = itk::SmartPointer<const Self>; + /** @} */ + + /** @name Standard macro + * @{ + */ + itkNewMacro(Self); + itkTypeMacro(PantexTextureExtraction, otb::Application); + /** @} */ + + using ExtractorFilterType = + otb::MultiToMonoChannelExtractROI<FloatVectorImageType::InternalPixelType, FloatVectorImageType::InternalPixelType>; + using PanTexTextureFilterType = otb::ScalarImageToPanTexTextureFilter<FloatImageType, FloatImageType>; + using MinMaxImageFilterType = otb::StreamingMinMaxImageFilter<FloatImageType>; + +private: + void DoInit() override + { + SetName("PantexTextureExtraction"); + SetDescription("Computes Pantex textural features on the selected channel of the input image"); + + SetDocLongDescription( + "This application computes a texture-derived built-up presence index (PanTex) from textural" + "characteristics of scalar images. This is a contrast textural measure based on co-occurance." + ); + + SetDocLimitations("None"); + + SetDocAuthors("OTB-Team"); + SetDocSeeAlso(" Pesari, M., A. Gerhardinger, F. Kayitakire. 2008. A robust built-up area precense" + " index by anisotropic rotation-invariant textural measure." + " IEEE Journal of selected topics in applied earth observations and remote sensing.Vol1, NO3."); + + AddDocTag(Tags::FeatureExtraction); + AddDocTag("Textures"); + + AddParameter(ParameterType_InputImage, "in", "Input Image"); + SetParameterDescription("in", "The input image to compute the features on."); + + AddParameter(ParameterType_Int, "channel", "Selected Channel"); + SetParameterDescription("channel", "The selected channel index"); + SetDefaultParameterInt("channel", 1); + SetMinimumParameterIntValue("channel", 1); + + AddParameter(ParameterType_OutputImage, "out", "Output Image"); + SetParameterDescription("out", "Output image containing the selected texture features."); + + AddParameter(ParameterType_Float, "min", "Image minimum"); + SetParameterDescription("min", "Input image minimum. If this parameter is not set, the application will compute " + "the minumum of the image."); + MandatoryOff("min"); + + AddParameter(ParameterType_Float, "max", "Image maximum"); + SetParameterDescription("max", "Input image maximum. If this parameter is not set, the application will compute " + "the maximum of the image."); + MandatoryOff("max"); + + AddParameter(ParameterType_Int, "sradx", "Window radius (x direction)"); + SetParameterDescription("sradx", "Radius of the window on which textures are computed (x direction)"); + SetMinimumParameterIntValue("sradx", 0); + SetDefaultParameterInt("sradx", 4); + + AddParameter(ParameterType_Int, "srady", "Window radius (y direction)"); + SetParameterDescription("srady", "Radius of the window on which textures are computed (y direction)"); + SetMinimumParameterIntValue("srady", 0); + SetDefaultParameterInt("srady", 4); + + AddParameter(ParameterType_Int, "nbin", "Number of bins per axis for histogram generation"); + SetParameterDescription("nbin", "Number of bins per axis for histogram generation " + "(number of gray levels considered in the computation of co-occurance)."); + SetDefaultParameterInt("nbin", 8); + + AddRAMParameter(); + + // Doc example parameter settings + SetDocExampleParameterValue("in", "qb_RoadExtract.tif"); + SetDocExampleParameterValue("channel", "2"); + SetDocExampleParameterValue("min", "0"); + SetDocExampleParameterValue("max", "255"); + SetDocExampleParameterValue("nbin", "8"); + SetDocExampleParameterValue("srady", "4"); + SetDocExampleParameterValue("sradx", "4"); + + SetOfficialDocLink(); + } + + + void DoUpdateParameters() override + { + // Nothing to do here : all parameters are independent + } + + void DoExecute() override + { + auto inImage = GetParameterImage("in"); + inImage->UpdateOutputInformation(); + + if ((unsigned int)GetParameterInt("channel") > inImage->GetNumberOfComponentsPerPixel()) + { + itkExceptionMacro(<< "The specified channel index is invalid."); + } + + auto extractorFilter = ExtractorFilterType::New(); + extractorFilter->SetInput(inImage); + extractorFilter->SetStartX(inImage->GetLargestPossibleRegion().GetIndex(0)); + extractorFilter->SetStartY(inImage->GetLargestPossibleRegion().GetIndex(1)); + extractorFilter->SetSizeX(inImage->GetLargestPossibleRegion().GetSize(0)); + extractorFilter->SetSizeY(inImage->GetLargestPossibleRegion().GetSize(1)); + extractorFilter->SetChannel(GetParameterInt("channel")); + + auto textureFilter = PanTexTextureFilterType::New(); + + textureFilter->SetNumberOfBinsPerAxis(GetParameterInt("nbin")); + textureFilter->SetRadius( {(unsigned int) GetParameterInt("sradx"), + (unsigned int) GetParameterInt("srady")} ); + + // Compute min and max only if one the corresponding parameter has not been set + if (!HasValue("min") || !HasValue("max")) + { + auto minMaxFilter = MinMaxImageFilterType::New(); + minMaxFilter->SetInput(extractorFilter->GetOutput()); + minMaxFilter->Update(); + + if (!HasValue("min")) + otbAppLogINFO(<< "Computed Minimum: " << minMaxFilter->GetMinimum()); + + if (!HasValue("max")) + otbAppLogINFO(<< "Computed Maximum: " << minMaxFilter->GetMaximum()); + + textureFilter->SetInputImageMinimum(HasValue("min") ? GetParameterFloat("min") + : minMaxFilter->GetMinimum()); + textureFilter->SetInputImageMaximum(HasValue("max") ? GetParameterFloat("max") + : minMaxFilter->GetMaximum()); + + } + else + { + textureFilter->SetInputImageMinimum(GetParameterFloat("min")); + textureFilter->SetInputImageMaximum(GetParameterFloat("max")); + } + + textureFilter->SetInput(extractorFilter->GetOutput()); + SetParameterOutputImage("out", textureFilter->GetOutput()); + + RegisterPipeline(); + } +}; + +} +} + +OTB_APPLICATION_EXPORT(otb::Wrapper::PantexTextureExtraction) diff --git a/Modules/Applications/AppTextures/otb-module.cmake b/Modules/Applications/AppTextures/otb-module.cmake index 06e557db237b03268abbcd38b50a4a0ce85c4824..1f5503d142000cbe2847b19b5bc7d8e05d48b377 100644 --- a/Modules/Applications/AppTextures/otb-module.cmake +++ b/Modules/Applications/AppTextures/otb-module.cmake @@ -27,6 +27,7 @@ otb_module(OTBAppTextures OTBImageBase OTBApplicationEngine OTBImageManipulation + OTBStatistics OTBObjectList TEST_DEPENDS diff --git a/Modules/Applications/AppTextures/test/CMakeLists.txt b/Modules/Applications/AppTextures/test/CMakeLists.txt index b4830b38dd70b81b9510212365152cb17df7627c..074c1d969534eee1a8f9ca30156c11d3a30650e1 100644 --- a/Modules/Applications/AppTextures/test/CMakeLists.txt +++ b/Modules/Applications/AppTextures/test/CMakeLists.txt @@ -43,3 +43,17 @@ otb_test_application(NAME apTvFESFSTextureExtraction ${BASELINE}/apTvFESFSTextureExtraction.tif ${TEMP}/apTvFESFSTextureExtraction.tif) + +#----------- PantexTextureExtraction TESTS ---------------- +otb_test_application(NAME apTvFEPantexTextureExtraction + APP PantexTextureExtraction + OPTIONS -in ${INPUTDATA}/QB_Toulouse_Ortho_PAN.tif + -channel 1 + -out ${TEMP}/apTvFEPantexTextureExtraction.tif + -nbin 8 + -sradx 4 + -srady 4 + VALID --compare-image ${NOTOL} + ${BASELINE}/apTvFEPantexTextureExtraction.tif + ${TEMP}/apTvFEPantexTextureExtraction.tif) + diff --git a/Modules/Feature/Textures/include/otbGreyLevelCooccurrenceIndexedList.h b/Modules/Feature/Textures/include/otbGreyLevelCooccurrenceIndexedList.h index 4638058c2bdd62e01e2ca71c4ad7e3a6b35e6658..b2b72dfbed4af01ff0b8570526d97622caa472d0 100644 --- a/Modules/Feature/Textures/include/otbGreyLevelCooccurrenceIndexedList.h +++ b/Modules/Feature/Textures/include/otbGreyLevelCooccurrenceIndexedList.h @@ -41,7 +41,7 @@ namespace otb * iteration over the given input image. This class keep an internal itk::Array * as a lookup array with size as [nbbins x nbbins]. The lookup array stores * position CooccurrencePairType in the VectorType. It ensures us that all elements -* in Vector are unqiue in terms of the index value in the pair. For any given +* in Vector are unique in terms of the index value in the pair. For any given * pixel index, -1 value indicates zero existence of the index in the * VectorType. This avoid searching all elements in VectorType for each pixel * index added during neighborhood iterator. It is also used to decide wheather @@ -136,9 +136,7 @@ public: protected: GreyLevelCooccurrenceIndexedList(); - ~GreyLevelCooccurrenceIndexedList() override - { - } + ~GreyLevelCooccurrenceIndexedList() override = default; /** create a cooccurrence pair with given index and frequency = 1 * value. Next occurrence of same index is checked via m_LookupArray and the diff --git a/Modules/Feature/Textures/include/otbScalarImageToPanTexTextureFilter.h b/Modules/Feature/Textures/include/otbScalarImageToPanTexTextureFilter.h index cbfebfba92b837b0c531c82db178514cebbcc5cb..0863e616ae32a206cba9eb3f3030f0a73fea1321 100644 --- a/Modules/Feature/Textures/include/otbScalarImageToPanTexTextureFilter.h +++ b/Modules/Feature/Textures/include/otbScalarImageToPanTexTextureFilter.h @@ -113,7 +113,7 @@ protected: /** Constructor */ ScalarImageToPanTexTextureFilter(); /** Destructor */ - ~ScalarImageToPanTexTextureFilter() override; + ~ScalarImageToPanTexTextureFilter() override = default; /** Generate the input requested region */ void GenerateInputRequestedRegion() override; /** Parallel textures extraction */ diff --git a/Modules/Feature/Textures/include/otbScalarImageToPanTexTextureFilter.hxx b/Modules/Feature/Textures/include/otbScalarImageToPanTexTextureFilter.hxx index 9a4457c0b78116818de1ebd0032ccfe5f7515273..93a2c826b348103b23827afbd8c95cf8be5c8e39 100644 --- a/Modules/Feature/Textures/include/otbScalarImageToPanTexTextureFilter.hxx +++ b/Modules/Feature/Textures/include/otbScalarImageToPanTexTextureFilter.hxx @@ -37,36 +37,9 @@ ScalarImageToPanTexTextureFilter<TInputImage, TOutputImage>::ScalarImageToPanTex // There are 1 output corresponding to the Pan Tex texture indice this->SetNumberOfRequiredOutputs(1); - // Fill the offset list for contrast computation - OffsetType off; - off[0] = 0; - off[1] = 1; - m_OffsetList.push_back(off); //(0, 1) - off[1] = 2; - m_OffsetList.push_back(off); //(0, 2) - off[0] = 1; - off[1] = -2; - m_OffsetList.push_back(off); //(1, -2) - off[1] = -1; - m_OffsetList.push_back(off); //(1, -1) - off[1] = 0; - m_OffsetList.push_back(off); //(1, 0) - off[1] = 1; - m_OffsetList.push_back(off); //(1, 1) - off[1] = 2; - m_OffsetList.push_back(off); //(1, 2) - off[0] = 2; - off[1] = -1; - m_OffsetList.push_back(off); //(2, -1) - off[1] = 0; - m_OffsetList.push_back(off); //(2, 0) - off[1] = 1; - m_OffsetList.push_back(off); //(2, 1) -} - -template <class TInputImage, class TOutputImage> -ScalarImageToPanTexTextureFilter<TInputImage, TOutputImage>::~ScalarImageToPanTexTextureFilter() -{ + // Ten offsets are selected for contrast computation (2 pixels displacement grid, and given that the + // co-occurance matrix is symmetric + m_OffsetList = { {0, 1}, {0, 2}, {1, -2}, {1, -1}, {1, 0}, {1, 1}, {1, 2}, {2, -1}, {2, 0}, {2, 1} }; } template <class TInputImage, class TOutputImage> @@ -93,9 +66,7 @@ void ScalarImageToPanTexTextureFilter<TInputImage, TOutputImage>::GenerateInputR InputRegionType inputRequestedRegion = outputRequestedRegion; // Apply the radius - SizeType maxOffsetSize; - maxOffsetSize[0] = 2; - maxOffsetSize[1] = 2; + SizeType maxOffsetSize = {2, 2}; inputRequestedRegion.PadByRadius(m_Radius + maxOffsetSize); // Try to apply the requested region to the input image @@ -123,8 +94,6 @@ void ScalarImageToPanTexTextureFilter<TInputImage, TOutputImage>::ThreadedGenera OutputImagePointerType outputPtr = this->GetOutput(); itk::ImageRegionIteratorWithIndex<OutputImageType> outputIt(outputPtr, outputRegionForThread); - - // Go to begin outputIt.GoToBegin(); // Set-up progress reporting @@ -154,13 +123,11 @@ void ScalarImageToPanTexTextureFilter<TInputImage, TOutputImage>::ThreadedGenera inputIndex[dim] = outputIt.GetIndex()[dim] - m_Radius[dim]; inputSize[dim] = 2 * m_Radius[dim] + 1; } - // Build the input region - InputRegionType inputRegion; - inputRegion.SetIndex(inputIndex); - inputRegion.SetSize(inputSize); + + // Build the input region + InputRegionType inputRegion = {inputIndex, inputSize}; inputRegion.Crop(inputPtr->GetRequestedRegion()); - SizeType neighborhoodRadius; /** calculate minimum offset and set it as neighborhood radius **/ unsigned int minRadius = 0; @@ -178,8 +145,8 @@ void ScalarImageToPanTexTextureFilter<TInputImage, TOutputImage>::ThreadedGenera GLCIList->Initialize(m_NumberOfBinsPerAxis, m_InputImageMinimum, m_InputImageMaximum); typedef itk::ConstNeighborhoodIterator<InputImageType> NeighborhoodIteratorType; - NeighborhoodIteratorType neighborIt; - neighborIt = NeighborhoodIteratorType(neighborhoodRadius, inputPtr, inputRegion); + NeighborhoodIteratorType neighborIt = NeighborhoodIteratorType(neighborhoodRadius, inputPtr, inputRegion); + for (neighborIt.GoToBegin(); !neighborIt.IsAtEnd(); ++neighborIt) { const InputPixelType centerPixelIntensity = neighborIt.GetCenterPixel(); @@ -201,8 +168,8 @@ void ScalarImageToPanTexTextureFilter<TInputImage, TOutputImage>::ThreadedGenera constVectorIt = glcVector.begin(); while (constVectorIt != glcVector.end()) { - CooccurrenceIndexType index = (*constVectorIt).first; - RelativeFrequencyType frequency = (*constVectorIt).second / totalFrequency; + CooccurrenceIndexType index = constVectorIt->first; + RelativeFrequencyType frequency = constVectorIt->second / totalFrequency; inertia += (index[0] - index[1]) * (index[0] - index[1]) * frequency; ++constVectorIt; }