diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..0443e71fc509dd4078ea4346c1106830beee6316 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8.9) + +project(OTBSLIC) + +if(NOT OTB_SOURCE_DIR) + find_package(OTB REQUIRED) + list(APPEND CMAKE_MODULE_PATH ${OTB_CMAKE_DIR}) + include(OTBModuleExternal) +else() + otb_module_impl() +endif() diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..73dec0b3ed22bd55817154c4386ad9d70bf14fa4 --- /dev/null +++ b/app/CMakeLists.txt @@ -0,0 +1,7 @@ +message(${${otb-module}_LIBRARIES}) +OTB_CREATE_APPLICATION( + NAME SLIC + SOURCES + otbSLIC.cxx + LINK_LIBRARIES ${${otb-module}_LIBRARIES} +) diff --git a/app/CMakeLists.txt~ b/app/CMakeLists.txt~ new file mode 100644 index 0000000000000000000000000000000000000000..646f32a74defe12cc1961060ee877707b822e450 --- /dev/null +++ b/app/CMakeLists.txt~ @@ -0,0 +1,7 @@ +message(${${otb-module}_LIBRARIES}) +OTB_CREATE_APPLICATION( + NAME SLIC + SOURCES + otbSLIC.cxx + LINK_LIBRARIES ${${otb-module}_LIBRARIES} OTBSLIC +) diff --git a/app/otbSLIC.cxx b/app/otbSLIC.cxx new file mode 100644 index 0000000000000000000000000000000000000000..b39390b74b4e3ad772dda6a8df1acc7bf36a4e97 --- /dev/null +++ b/app/otbSLIC.cxx @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2018 Centre National d'Etudes Spatiales (CNES) + * + * This file is part a Remote Module for Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ +#include "otbWrapperApplication.h" +#include "otbWrapperApplicationFactory.h" +#include "otbImage.h" +#include "otbMultiToMonoChannelExtractROI.h" +#include "otbOGRDataSourceWrapper.h" +#include "otbOGRFeatureWrapper.h" + +#include "otbSLICScheduler.h" +#include "otbImageFileReader.h" +#include "otbImageFileWriter.h" +// #include "otbSegmentCharacteristicsFilter.h" +#include "otbPersistentFilterStreamingDecorator.h" +#include "itkLabelToRGBImageFilter.h" + +namespace otb +{ + namespace Wrapper + { + class SLIC : public Application + { + public: + typedef SLIC Self; + typedef Application Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + typedef otb::VectorImage<float> VectorImageType; + typedef unsigned int LabelType; + typedef otb::Image<LabelType> LabelImageType; + typedef otb::SLICScheduler<VectorImageType, LabelImageType> SLICSchedulerType; + typedef otb::ImageFileReader<VectorImageType> ReaderType; + typedef otb::ImageFileReader<LabelImageType> LabelReaderType; + typedef otb::ImageFileWriter<LabelImageType> WriterType; + typedef typename otb::VectorImage<double>::PixelType SampleType; + + typedef otb::VectorImage<unsigned char> RGBImageType; + typedef RGBImageType::PixelType RGBPixelType; + typedef otb::ImageFileWriter<RGBImageType> RGBWriterType; + + // Segmentation contrast maximisation LUT + typedef itk::LabelToRGBImageFilter <LabelImageType, RGBImageType> LabelToRGBFilterType; + + itkNewMacro(Self); + itkTypeMacro(Merging, otb::Application); + + private: + + + //Tiling modes + enum TilingMode + { + AUTO, + MANUAL + }; + + void DoInit() + { + SetName("SLIC"); + SetDescription(""); + + SetDocName(""); + SetDocLongDescription(""); + SetDocLimitations(""); + SetDocAuthors(""); + SetDocSeeAlso(""); + + AddParameter(ParameterType_InputImage, "in", "Input image"); + SetParameterDescription( "in", "The input image." ); + + AddParameter(ParameterType_OutputImage, "out", "Output raster"); + SetParameterDescription( "out", "Output raster containing the segment label IDs" ); + SetDefaultOutputPixelType("out",ImagePixelType_uint32); + + AddParameter(ParameterType_Float,"spw","Spatial Width"); + SetParameterDescription("spw","Initial width of segment grid, in pixels. Is approximately the average width of final segments"); + SetDefaultParameterFloat("spw",100); + SetMinimumParameterFloatValue("spw",2); + + AddParameter(ParameterType_Float,"dw","Distance weight"); + SetParameterDescription("dw","Importance of spatial distance w.r.t. spectral distance"); + SetDefaultParameterFloat("dw",1); + SetMinimumParameterFloatValue("dw",0); + + AddParameter(ParameterType_Int,"maxit","Maximum number of iterations"); + SetParameterDescription("maxit","Maximum number of iterations of the SLIC algorithm."); + SetDefaultParameterInt("maxit",100); + SetMinimumParameterIntValue("maxit",1); + + AddParameter(ParameterType_Float,"thresh","Stopping threshold"); + SetParameterDescription("thresh","Minimum residial for stopping the algorithm."); + SetDefaultParameterFloat("thresh",0.001); + SetMinimumParameterFloatValue("thresh",0); + + AddParameter(ParameterType_Float,"margin","Margin for stabilizing"); + SetParameterDescription("margin","Margin in superpixel widths for stabilizing the scaled algorithm"); + SetDefaultParameterFloat("margin",3); + SetMinimumParameterFloatValue("margin",0); + + AddParameter(ParameterType_Directory, "tmpdir", "Temporary directory"); + SetParameterDescription( "tmpdir", "Temporary directory for storing parts of the output." ); + + AddParameter(ParameterType_Choice,"tiling","Tiling mode (auto or manual)"); + AddChoice("tiling.auto","Automatic tiling mode"); + AddChoice("tiling.manual","Manual tiling mode"); + + AddParameter(ParameterType_Int, "tiling.auto.ram", "Available ram (Mb)"); + SetParameterDescription("tiling.auto.ram", "Available ram to be used per MPI process in Mb"); + SetDefaultParameterInt("tiling.auto.ram", 128); + SetMinimumParameterIntValue("tiling.auto.ram", 0); + + AddParameter(ParameterType_Int, "tiling.manual.nx", "Number of tiles (X-axis)"); + SetParameterDescription("tiling.manual.nx", "Number of tiles along the X-axis."); + SetMinimumParameterIntValue("tiling.manual.nx", 1); + + AddParameter(ParameterType_Int, "tiling.manual.ny", "Number of tiles (Y-axis)"); + SetParameterDescription("tiling.manual.ny", "Number of tiles along the Y-axis."); + SetMinimumParameterIntValue("tiling.manual.ny", 1); + + GDALSetCacheMax(0); + } + + void DoUpdateParameters() + { + } + + void DoExecute() + { + + + typename otb::MPIConfig::Pointer mpiConfig = otb::MPIConfig::Instance(); + const unsigned int myRank = mpiConfig->GetMyRank(); + + VectorImageType::Pointer imageIn = GetParameterImage("in"); + const std::string inputName = GetParameterString("in"); + imageIn->UpdateOutputInformation(); + const std::string outputName = GetParameterString("out"); + const unsigned int spatialWidth = GetParameterFloat("spw"); + const double distanceWeight = GetParameterFloat("dw"); + const unsigned int maxIterations = GetParameterInt("maxit"); + const double threshold = GetParameterFloat("thresh"); + unsigned int margin = GetParameterInt("margin"); + unsigned int nbTilesX = 1; + unsigned int nbTilesY = 1; + + std::stringstream s_prefix; + s_prefix << GetParameterString("tmpdir") << "/" << "temp" ; + std::string prefix = s_prefix.str(); + s_prefix << ".vrt"; + std::string outputVrt = s_prefix.str(); + + switch(GetParameterInt("tiling")) + { + case AUTO : + { + const unsigned int maxMemory = GetParameterInt("tiling.auto.ram"); + //Auto Tiling mode + const unsigned int X = imageIn->GetLargestPossibleRegion().GetSize()[0]; + const unsigned int Y = imageIn->GetLargestPossibleRegion().GetSize()[1]; + const unsigned int nbComps = imageIn->GetNumberOfComponentsPerPixel(); + const unsigned int pin = 2*nbComps; //because reader casts to unsigned short + const unsigned int pout = 4; //unsigned int label + const unsigned int pc = (nbComps+2)*8+4; //centroid size + + //Reading config + double alpha = 2*pin; + double beta = (2*pin + pout)*2*spatialWidth*margin; + const unsigned int readingNx = vcl_ceil(X*alpha/(vcl_sqrt(4*beta*beta + alpha*maxMemory*1024*1024) - beta)); + const unsigned int readingNy = vcl_ceil(Y*alpha/(vcl_sqrt(4*beta*beta + alpha*maxMemory*1024*1024) - beta)); + + //Algo config + alpha = pin+pout+(double)2*pc/spatialWidth/spatialWidth; + beta = (pin + pout)*2*spatialWidth*margin; + const unsigned int algoNx = vcl_ceil(X*alpha/(vcl_sqrt(4*beta*beta + alpha*maxMemory*1024*1024) - beta)); + const unsigned int algoNy = vcl_ceil(Y*alpha/(vcl_sqrt(4*beta*beta + alpha*maxMemory*1024*1024) - beta)); + + nbTilesX = readingNx > algoNx ? readingNx : algoNx; + nbTilesY = readingNy > algoNy ? readingNy : algoNy; + + std::cout << "Auto tiling mode selected, available RAM = " << maxMemory << "MB" << std::endl; + std::cout << "Number of x tiles = "<< nbTilesX << std::endl; + std::cout << "Number of y tiles = "<< nbTilesY << std::endl; + break; + }; + case MANUAL : + { + nbTilesX = GetParameterInt("tiling.manual.nx"); + nbTilesY = GetParameterInt("tiling.manual.ny"); + }; + } + //SlicScheduler will write the tiled images and a .vrt in the tmpdir + std::cout << "Starting segmentation" << "\n"; + SLICSchedulerType::Pointer slicScheduler = SLICSchedulerType::New(); + slicScheduler->SetInputImage(imageIn); + slicScheduler->SetInputName(inputName); + slicScheduler->SetPrefix(prefix); + slicScheduler->SetSpatialWidth(spatialWidth); + slicScheduler->SetSpatialDistanceWeight(distanceWeight); + slicScheduler->SetMaxIterationNumber(maxIterations); + slicScheduler->SetThreshold(threshold); + slicScheduler->SetNbTilesX(nbTilesX); + slicScheduler->SetNbTilesY(nbTilesY); + slicScheduler->SetMargin(margin); + slicScheduler->Run(); + + std::cout << "Segmentation finished" << "\n"; + + //Rank 0 thread must have time to write the .vrt + mpiConfig->barrier(); + + //Convert .vrt into .tif using MPI : requires SPTW + LabelReaderType::Pointer labelReader = LabelReaderType::New(); + labelReader->SetFileName(outputVrt); + labelReader->Update(); + SetParameterOutputImage<UInt32ImageType> ("out",dynamic_cast<UInt32ImageType*>(labelReader->GetOutput())); + + } + }; + } +} + +OTB_APPLICATION_EXPORT(otb::Wrapper::SLIC) + + diff --git a/app/otbSLIC.cxx~ b/app/otbSLIC.cxx~ new file mode 100644 index 0000000000000000000000000000000000000000..45547e861caf856026d35b807e45f9c3590d34ea --- /dev/null +++ b/app/otbSLIC.cxx~ @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2018 Centre National d'Etudes Spatiales (CNES) + * + * This file is part a Remote Module for Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ +#include "otbWrapperApplication.h" +#include "otbWrapperApplicationFactory.h" +#include "otbImage.h" +#include "otbMultiToMonoChannelExtractROI.h" +#include "otbOGRDataSourceWrapper.h" +#include "otbOGRFeatureWrapper.h" + +#include "otbSLICScheduler.h" +#include "otbImageFileReader.h" +#include "otbImageFileWriter.h" +// #include "otbSegmentCharacteristicsFilter.h" +#include "otbPersistentFilterStreamingDecorator.h" +#include "itkLabelToRGBImageFilter.h" + +namespace otb +{ + namespace Wrapper + { + class SLIC : public Application + { + public: + typedef SLIC Self; + typedef Application Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + typedef otb::VectorImage<float> VectorImageType; + typedef unsigned int LabelType; + typedef otb::Image<LabelType> LabelImageType; + typedef otb::SLICScheduler<VectorImageType, LabelImageType> SLICSchedulerType; + typedef otb::ImageFileReader<VectorImageType> ReaderType; + typedef otb::ImageFileReader<LabelImageType> LabelReaderType; + typedef otb::ImageFileWriter<LabelImageType> WriterType; + typedef typename otb::VectorImage<double>::PixelType SampleType; + + typedef otb::VectorImage<unsigned char> RGBImageType; + typedef RGBImageType::PixelType RGBPixelType; + typedef otb::ImageFileWriter<RGBImageType> RGBWriterType; + + // Segmentation contrast maximisation LUT + typedef itk::LabelToRGBImageFilter <LabelImageType, RGBImageType> LabelToRGBFilterType; + + itkNewMacro(Self); + itkTypeMacro(Merging, otb::Application); + + private: + + + //Tiling modes + enum TilingMode + { + AUTO, + MANUAL + }; + + void DoInit() + { + SetName("SLIC"); + SetDescription(""); + + SetDocName(""); + SetDocLongDescription(""); + SetDocLimitations(""); + SetDocAuthors(""); + SetDocSeeAlso(""); + + AddParameter(ParameterType_InputImage, "in", "Input image"); + SetParameterDescription( "in", "The input image." ); + + AddParameter(ParameterType_OutputImage, "out", "Output raster"); + SetParameterDescription( "out", "Output raster containing the segment label IDs" ); + SetDefaultOutputPixelType("out",ImagePixelType_uint32); + + AddParameter(ParameterType_Float,"spw","Spatial Width"); + SetParameterDescription("spw","Initial width of segment grid, in pixels. Is approximately the average width of final segments"); + SetDefaultParameterFloat("spw",100); + SetMinimumParameterFloatValue("spw",2); + + AddParameter(ParameterType_Float,"dw","Distance weight"); + SetParameterDescription("dw","Importance of spatial distance w.r.t. spectral distance"); + SetDefaultParameterFloat("dw",1); + SetMinimumParameterFloatValue("dw",0); + + AddParameter(ParameterType_Int,"maxit","Maximum number of iterations"); + SetParameterDescription("maxit","Maximum number of iterations of the SLIC algorithm."); + SetDefaultParameterInt("maxit",100); + SetMinimumParameterIntValue("maxit",1); + + AddParameter(ParameterType_Float,"thresh","Stopping threshold"); + SetParameterDescription("thresh","Minimum residial for stopping the algorithm."); + SetDefaultParameterFloat("thresh",0.001); + SetMinimumParameterFloatValue("thresh",0); + + AddParameter(ParameterType_Float,"margin","Margin for stabilizing"); + SetParameterDescription("margin","Margin in superpixel widths for stabilizing the scaled algorithm"); + SetDefaultParameterFloat("margin",3); + SetMinimumParameterFloatValue("margin",0); + + AddParameter(ParameterType_Directory, "tmpdir", "Temporary directory"); + SetParameterDescription( "tmpdir", "Temporary directory for storing parts of the output." ); + + AddParameter(ParameterType_Choice,"tiling","Tiling mode (auto or manual)"); + AddChoice("tiling.auto","Automatic tiling mode"); + AddChoice("tiling.manual","Manual tiling mode"); + + AddParameter(ParameterType_Int, "tiling.auto.ram", "Available ram (Mb)"); + SetParameterDescription("tiling.auto.ram", "Available ram to be used per MPI process in Mb"); + SetDefaultParameterInt("tiling.auto.ram", 128); + SetMinimumParameterIntValue("tiling.auto.ram", 0); + + AddParameter(ParameterType_Int, "tiling.manual.nx", "Number of tiles (X-axis)"); + SetParameterDescription("tiling.manual.nx", "Number of tiles along the X-axis."); + SetMinimumParameterIntValue("tiling.manual.nx", 1); + + AddParameter(ParameterType_Int, "tiling.manual.ny", "Number of tiles (Y-axis)"); + SetParameterDescription("tiling.manual.ny", "Number of tiles along the Y-axis."); + SetMinimumParameterIntValue("tiling.manual.ny", 1); + + GDALSetCacheMax(0); + } + + void DoUpdateParameters() + { + } + + void DoExecute() + { + + + typename otb::MPIConfig::Pointer mpiConfig = otb::MPIConfig::Instance(); + const unsigned int myRank = mpiConfig->GetMyRank(); + + VectorImageType::Pointer imageIn = GetParameterImage("in"); + const std::string inputName = GetParameterString("in"); + imageIn->UpdateOutputInformation(); + const std::string outputName = GetParameterString("out"); + const unsigned int spatialWidth = GetParameterFloat("spw"); + const double distanceWeight = GetParameterFloat("dw"); + const unsigned int maxIterations = GetParameterInt("maxit"); + const double threshold = GetParameterFloat("thresh"); + unsigned int margin = GetParameterInt("margin"); + unsigned int nbTilesX = 1; + unsigned int nbTilesY = 1; + + std::stringstream s_prefix; + s_prefix << GetParameterString("tmpdir") << "/" << "temp" ; + std::string prefix = s_prefix.str(); + s_prefix << ".vrt"; + std::string outputVrt = s_prefix.str(); + + switch(GetParameterInt("tiling")) + { + case AUTO : + { + unsigned int maxMemory = GetParameterInt("tiling.auto.ram"); + //Auto Tiling mode + const unsigned int X = imageIn->GetLargestPossibleRegion().GetSize()[0]; + const unsigned int Y = imageIn->GetLargestPossibleRegion().GetSize()[1]; + const unsigned int nbComps = imageIn->GetNumberOfComponentsPerPixel(); + const unsigned int pin = 2*nbComps; //because reader casts to unsigned short + const unsigned int pout = 4; //unsigned int label + const unsigned int pc = (nbComps+2)*8+4; //centroid size + + //Reading config + double alpha = 2*pin; + double beta = (2*pin + pout)*2*spatialWidth*margin; + const unsigned int readingNx = vcl_ceil(X*alpha/(vcl_sqrt(4*beta*beta + alpha*maxMemory*1024*1024) - beta)); + const unsigned int readingNy = vcl_ceil(Y*alpha/(vcl_sqrt(4*beta*beta + alpha*maxMemory*1024*1024) - beta)); + + //Algo config + alpha = pin+pout+(double)2*pc/spatialWidth/spatialWidth; + beta = (pin + pout)*2*spatialWidth*margin; + const unsigned int algoNx = vcl_ceil(X*alpha/(vcl_sqrt(4*beta*beta + alpha*maxMemory*1024*1024) - beta)); + const unsigned int algoNy = vcl_ceil(Y*alpha/(vcl_sqrt(4*beta*beta + alpha*maxMemory*1024*1024) - beta)); + + nbTilesX = readingNx > algoNx ? readingNx : algoNx; + nbTilesY = readingNy > algoNy ? readingNy : algoNy; + + std::cout << "Auto tiling mode selected, available RAM = " << maxMemory << "MB" << std::endl; + std::cout << "Number of x tiles = "<< nbTilesX << std::endl; + std::cout << "Number of y tiles = "<< nbTilesY << std::endl; + break; + }; + case MANUAL : + { + nbTilesX = GetParameterInt("tiling.manual.nx"); + nbTilesY = GetParameterInt("tiling.manual.ny"); + }; + } + std::cout << "SLIC real 3" << "\n"; + SLICSchedulerType::Pointer slicScheduler = SLICSchedulerType::New(); + slicScheduler->SetInputImage(imageIn); + slicScheduler->SetInputName(inputName); + slicScheduler->SetPrefix(prefix); + slicScheduler->SetSpatialWidth(spatialWidth); + slicScheduler->SetSpatialDistanceWeight(distanceWeight); + slicScheduler->SetMaxIterationNumber(maxIterations); + slicScheduler->SetThreshold(threshold); + slicScheduler->SetNbTilesX(nbTilesX); + slicScheduler->SetNbTilesY(nbTilesY); + slicScheduler->SetMargin(margin); + slicScheduler->Run(); + + std::cout << "App finished" << "\n"; + + // if (myRank == 0) { + + mpiConfig->barrier(); + LabelReaderType::Pointer labelReader = LabelReaderType::New(); + labelReader->SetFileName(outputVrt); + labelReader->Update(); + + SetParameterOutputImage<UInt32ImageType> ("out",dynamic_cast<UInt32ImageType*>(labelReader->GetOutput())); + + //TODO cleanup temp dir + // } + } + }; + } +} + +OTB_APPLICATION_EXPORT(otb::Wrapper::SLIC) + + diff --git a/include/otbSLICFilter.h b/include/otbSLICFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..dd0907e2d897c5e910c22c2bebd3a139eb7a38f3 --- /dev/null +++ b/include/otbSLICFilter.h @@ -0,0 +1,154 @@ +#ifndef otbSLICFilter_h +#define otbSLICFilter_h + +#include "itkMacro.h" +//OTB, ITK includes +#include "otbVectorImage.h" +#include "otbImage.h" +#include "otbStreamingTraits.h" + +#include "itkImageRegionConstIteratorWithIndex.h" +#include "itkImageRegionIteratorWithIndex.h" +#include "itkImageRegionExclusionConstIteratorWithIndex.h" +#include "itkNeighborhoodIterator.h" + +#include "itkProcessObject.h" +//Own includes +#include "simplePointCalculator.h" + +//STD includes +#include <algorithm>//std::find +#include <unordered_set> +#include <unordered_map> +namespace otb{ + enum RelativePosition{ + POS_TOP, + POS_RIGHT, + POS_BOTTOM, + POS_LEFT + }; + + enum NeighborhoodRelativePosition{ + NBH_TOP, + NBH_TOP_RIGHT, + NBH_RIGHT, + NBH_BOTTOM_RIGHT, + NBH_BOTTOM, + NBH_BOTTOM_LEFT, + NBH_LEFT, + NBH_TOP_LEFT + }; + struct ProcessingTile + { + long int rows[2]; // lower and upper rows (-1 means that the row has not be considered) + long int columns[2]; // lower and upper columns (-1 means that the row has not be considered) + long int tileNeighbors[8]; // tile Neighbors at (top, top right, right, bottom right, bottom, bottom left, left, top left) + long int margin[4]; // Is there a margin at top, left, bottom or right + otb::VectorImage<double>::RegionType region; // The image region + + // Temporary files + std::string nodeFileName; + std::string edgeFileName; + std::string nodeMarginFileName; + std::string edgeMarginFileName; + }; + template <class TInputImage, class TOutputLabelImage> + class ITK_EXPORT SLICFilter : public itk::ProcessObject + { + public: + typedef SLICFilter Self; + typedef itk::ProcessObject 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(SLICFilter, itk::ProcessObject); + + /** ImageDimension constants */ + itkStaticConstMacro(ImageDimension, unsigned int, TInputImage::ImageDimension); + + typedef double RealType; + typedef typename TOutputLabelImage::PixelType LabelType; + typedef TOutputLabelImage OutputLabelImageType; + typedef TInputImage VectorImageType; + typedef typename TInputImage::RegionType RegionType; + typedef typename std::vector<double> SampleType; + typedef typename VectorImage<double>::PixelType PixelType; + typedef std::unordered_map<LabelType,SampleType> ListSampleType; + typedef std::unordered_map<LabelType,unsigned int> CountContainerType; + typedef std::vector<LabelType> NeighborhoodLabelsType; + typedef itk::ConstantBoundaryCondition<TOutputLabelImage> BoundaryConditionType; + typedef itk::NeighborhoodIterator<TOutputLabelImage,BoundaryConditionType> NeighborhoodLabelIteratorType; + typedef typename NeighborhoodLabelIteratorType::NeighborhoodType NeighborhoodType; + typedef typename NeighborhoodType::ConstIterator NeighborhoodContainerIteratorType; + + // Sets the spatial width, i.e. average root of size of the output superpixels + itkSetMacro(SpatialWidth,unsigned int); + + // Sets the spatial distance weight, i.e. the compacity/homogeneity ratio of SP + itkSetMacro(SpatialDistanceWeight,RealType); + + //Sets the max number of iterations + itkSetMacro(MaxIterationNumber,unsigned int); + + //Sets the residual threshold for stopping the algorithm + itkSetMacro(Threshold,RealType); + + //Desired number of superpixels along the X axis of the current tile + itkSetMacro(NbSPx, unsigned int); + + //Margin in superpixels + itkSetMacro(Margin, unsigned int); + + //Sets the input + using Superclass::SetInput; + void SetInput(const TInputImage* InputImagePtr); + + //Add image of margin to list of margins + void AddInputMargin(typename TOutputLabelImage::Pointer InputSegPtr); + //Set information regarding the current tile (size, origin, neighbors) + void SetTile(ProcessingTile const& _t); + //Clear the margin images + void ClearMargins(); + // //Returns the const image of region labels + const OutputLabelImageType * GetOutput() const; + // //Returns the image of region labels + OutputLabelImageType * GetOutput(); + //Write the segmentation of a region + template <class TImage> static void WriteImageRegion(typename TImage::Pointer inputPtr, typename TImage::Pointer inputImage, typename TImage::RegionType const& region); + + protected: + SLICFilter(); + ~SLICFilter() ITK_OVERRIDE; + void GenerateData() ITK_OVERRIDE; + void GenerateInputRequestedRegion() ITK_OVERRIDE; + TInputImage* GetInput(); + + private: + SLICFilter(const Self &); //purposely not implemented + void operator =(const Self&); //purposely not implemented + unsigned int m_SpatialWidth; + RealType m_SpatialDistanceWeight; + unsigned int m_MaxIterationNumber; + RealType m_Threshold; + ProcessingTile m_Tile; + unsigned int m_NbSPx; + unsigned int m_Margin; + std::vector<typename OutputLabelImageType::Pointer> m_InputSeg; + //Test to check if a pixel is in the corner of the tile (with margins) + static bool inImageCorner(std::vector<RegionType> const& corners, typename TOutputLabelImage::IndexType index); + //Method to recompute the new coordinates of a centroid when removing a value + static void removeFromCentroid(PixelType const& currentPixel, typename TInputImage::IndexType index, LabelType label, CountContainerType & count, ListSampleType & means); + //Method to recompute the new coordinates of a centroid when adding a value + static void addToCentroid(PixelType const& currentPixel, typename TInputImage::IndexType index, LabelType label, CountContainerType & count, ListSampleType & means); + + }; +} +#ifndef OTB_MANUAL_INSTANTIATION +#include "otbSLICFilter.txx" +#endif + +#endif diff --git a/include/otbSLICFilter.h~ b/include/otbSLICFilter.h~ new file mode 100644 index 0000000000000000000000000000000000000000..377e748f021a029b2f81e9870bf6f439eb53aefd --- /dev/null +++ b/include/otbSLICFilter.h~ @@ -0,0 +1,148 @@ +#ifndef otbSLICFilter_h +#define otbSLICFilter_h + +#include "itkMacro.h" +//OTB, ITK includes +#include "otbVectorImage.h" +#include "otbImage.h" +#include "otbStreamingTraits.h" + +#include "itkImageRegionConstIteratorWithIndex.h" +#include "itkImageRegionIteratorWithIndex.h" +#include "itkImageRegionExclusionConstIteratorWithIndex.h" +#include "itkNeighborhoodIterator.h" + +#include "itkProcessObject.h" +//Own includes +#include "simplePointCalculator.h" + +//STD includes +#include <algorithm>//std::find +#include <unordered_set> +#include <unordered_map> +namespace otb{ + enum RelativePosition{ + POS_TOP, + POS_RIGHT, + POS_BOTTOM, + POS_LEFT + }; + + enum NeighborhoodRelativePosition{ + NBH_TOP, + NBH_TOP_RIGHT, + NBH_RIGHT, + NBH_BOTTOM_RIGHT, + NBH_BOTTOM, + NBH_BOTTOM_LEFT, + NBH_LEFT, + NBH_TOP_LEFT + }; + struct ProcessingTile + { + long int rows[2]; // lower and upper rows (-1 means that the row has not be considered) + long int columns[2]; // lower and upper columns (-1 means that the row has not be considered) + long int tileNeighbors[8]; // tile Neighbors at (top, top right, right, bottom right, bottom, bottom left, left, top left) + long int margin[4]; // Is there a margin at top, left, bottom or right + otb::VectorImage<double>::RegionType region; // The image region + + // Temporary files + std::string nodeFileName; + std::string edgeFileName; + std::string nodeMarginFileName; + std::string edgeMarginFileName; + }; + template <class TInputImage, class TOutputLabelImage> + class ITK_EXPORT SLICFilter : public itk::ProcessObject + { + public: + typedef SLICFilter Self; + typedef itk::ProcessObject 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(SLICFilter, itk::ProcessObject); + + /** ImageDimension constants */ + itkStaticConstMacro(ImageDimension, unsigned int, TInputImage::ImageDimension); + + typedef double RealType; + typedef typename TOutputLabelImage::PixelType LabelType; + typedef TOutputLabelImage OutputLabelImageType; + typedef TInputImage VectorImageType; + typedef typename TInputImage::RegionType RegionType; + typedef typename std::vector<double> SampleType; + typedef typename VectorImage<double>::PixelType PixelType; + typedef std::unordered_map<LabelType,SampleType> ListSampleType; + typedef std::unordered_map<LabelType,unsigned int> CountContainerType; + typedef std::vector<LabelType> NeighborhoodLabelsType; + typedef itk::ConstantBoundaryCondition<TOutputLabelImage> BoundaryConditionType; + typedef itk::NeighborhoodIterator<TOutputLabelImage,BoundaryConditionType> NeighborhoodLabelIteratorType; + typedef typename NeighborhoodLabelIteratorType::NeighborhoodType NeighborhoodType; + typedef typename NeighborhoodType::ConstIterator NeighborhoodContainerIteratorType; + + // Sets the spatial width, i.e. average root of size of the output superpixels + itkSetMacro(SpatialWidth,unsigned int); + + // Sets the spatial distance weight, i.e. the compacity/homogeneity ratio of SP + itkSetMacro(SpatialDistanceWeight,RealType); + + //Sets the max number of iterations + itkSetMacro(MaxIterationNumber,unsigned int); + + //Sets the residual threshold for stopping the algorithm + itkSetMacro(Threshold,RealType); + + itkSetMacro(NbSPx, unsigned int); + + itkSetMacro(Margin, unsigned int); + + using Superclass::SetInput; + void SetInput(const TInputImage* InputImagePtr); + void AddInputMargin(typename TOutputLabelImage::Pointer InputSegPtr); + + void SetTile(ProcessingTile const& _t); + void ClearMargins(); + // //Returns the const image of region labels + const OutputLabelImageType * GetOutput() const; + // //Returns the image of region labels + OutputLabelImageType * GetOutput(); + template <class TImage> static void WriteImageRegion(typename TImage::Pointer inputPtr, typename TImage::Pointer inputImage, typename TImage::RegionType const& region); + + protected: + SLICFilter(); + + ~SLICFilter() ITK_OVERRIDE; + + void GenerateData() ITK_OVERRIDE; + + void GenerateInputRequestedRegion() ITK_OVERRIDE; + // void PrintLogLikelihood(); + TInputImage* GetInput(); + private: + SLICFilter(const Self &); //purposely not implemented + void operator =(const Self&); //purposely not implemented + unsigned int m_SpatialWidth; + RealType m_SpatialDistanceWeight; + unsigned int m_MaxIterationNumber; + RealType m_Threshold; + ProcessingTile m_Tile; + unsigned int m_NbSPx; + unsigned int m_Margin; + std::vector<typename OutputLabelImageType::Pointer> m_InputSeg; + + static bool inImageCorner(std::vector<RegionType> const& corners, typename TOutputLabelImage::IndexType index); + static void removeFromCentroid(PixelType const& currentPixel, typename TInputImage::IndexType index, LabelType label, CountContainerType & count, ListSampleType & means); + static void addToCentroid(PixelType const& currentPixel, typename TInputImage::IndexType index, LabelType label, CountContainerType & count, ListSampleType & means); + + }; +} +#ifndef OTB_MANUAL_INSTANTIATION +#include "otbSLICFilter.txx" +#endif + +#endif diff --git a/include/otbSLICFilter.txx b/include/otbSLICFilter.txx new file mode 100644 index 0000000000000000000000000000000000000000..a33f94f0b77fea73e95a47005568986d9af4db4e --- /dev/null +++ b/include/otbSLICFilter.txx @@ -0,0 +1,383 @@ +#ifndef otbSLICFilter_txx +#define otbSLICFilter_txx + +#include "otbSLICFilter.h" + +namespace otb{ + + template <class TInputImage, class TOutputLabelImage> + SLICFilter<TInputImage, TOutputLabelImage> + ::SLICFilter() : + m_SpatialWidth(100), + m_SpatialDistanceWeight(1), + m_MaxIterationNumber(10), + m_Threshold(0.1), + m_NbSPx(1), + m_Margin(0) + { + this->SetNumberOfRequiredInputs(1); + this->SetNumberOfRequiredOutputs(1); + this->SetNthOutput(0,TOutputLabelImage::New()); + } + + template <class TInputImage, class TOutputLabelImage> + SLICFilter<TInputImage, TOutputLabelImage> + ::~SLICFilter() + {} + + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::SetInput(const TInputImage* input) + { + this->ProcessObject::SetNthInput(0,const_cast<TInputImage*>(input)); + } + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::AddInputMargin(typename TOutputLabelImage::Pointer InputSegPtr) + { + m_InputSeg.push_back(InputSegPtr); + } + + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::SetTile(ProcessingTile const& _t) + { + m_Tile = _t; + } + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::ClearMargins() + { + m_InputSeg.clear(); + } + template <class TInputImage, class TOutputLabelImage> + TInputImage * + SLICFilter<TInputImage, TOutputLabelImage> + ::GetInput() + { + return static_cast<TInputImage *>(this->itk::ProcessObject::GetInput(0)); + } + template <class TInputImage, class TOutputLabelImage> + TOutputLabelImage * + SLICFilter<TInputImage, TOutputLabelImage> + ::GetOutput() + { + return static_cast<TOutputLabelImage *>(this->itk::ProcessObject::GetOutput(0)); + } + template <class TInputImage, class TOutputLabelImage> + const TOutputLabelImage * + SLICFilter<TInputImage, TOutputLabelImage> + ::GetOutput() const + { + return static_cast<TOutputLabelImage *>(this->itk::ProcessObject::GetOutput(0)); + } + + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::GenerateInputRequestedRegion(){ + this->GetOutput()->SetRegions(this->GetInput()->GetLargestPossibleRegion()); + } + template <class TInputImage, class TOutputLabelImage> + bool + SLICFilter<TInputImage, TOutputLabelImage> + ::inImageCorner(std::vector<RegionType> const& corners, typename TOutputLabelImage::IndexType index){ + for(RegionType cornerRegion : corners) + if(cornerRegion.IsInside(index)) + return true; + return false; + } + + + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::removeFromCentroid(PixelType const& currentPixel, typename TInputImage::IndexType index, LabelType label, CountContainerType & count, ListSampleType & means){ + const typename CountContainerType::iterator searchCount = count.find(label); + //Update with running formulas + //Update count + searchCount->second--; + //Update Means + const typename ListSampleType::iterator searchM = means.find(label); + const SampleType oldMeans = searchM->second; + SampleType newMeans = oldMeans; + const double N = searchCount->second;//Cast int to double + const unsigned int nbComps = newMeans.size() - 2 ; + for(unsigned int i = 0 ; i < nbComps ; ++i) + { + newMeans[i] += (oldMeans[i]-currentPixel[i])/N; + } + newMeans[nbComps] += (oldMeans[nbComps]-index[0])/N; + newMeans[nbComps+1] += (oldMeans[nbComps+1]-index[1])/N; + searchM->second = newMeans; + } + + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::addToCentroid(PixelType const& currentPixel, typename TInputImage::IndexType index, LabelType label, CountContainerType & count, ListSampleType & means){ + const typename CountContainerType::iterator searchCount = count.find(label); + //Update with running formulas + //Update count + searchCount->second++; + //Update Means + const typename ListSampleType::iterator searchM = means.find(label); + const SampleType oldMeans = searchM->second; + SampleType newMeans = oldMeans; + const double N = searchCount->second;//Cast int to double + const unsigned int nbComps = newMeans.size() - 2 ; + for(unsigned int i = 0 ; i < nbComps ; ++i) + { + newMeans[i] += (currentPixel[i]-oldMeans[i])/N; + } + newMeans[nbComps] += (index[0]-oldMeans[nbComps])/N; + newMeans[nbComps+1] += (index[1]-oldMeans[nbComps+1])/N; + searchM->second = newMeans; + } + + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::GenerateData() + { + this->GetOutput()->SetRegions(this->GetInput()->GetLargestPossibleRegion()); + this->GetOutput()->SetOrigin(this->GetInput()->GetOrigin()); + this->GetOutput()->SetSignedSpacing(this->GetInput()->GetSignedSpacing()); + this->GetOutput()->Allocate(); + this->GetOutput()->FillBuffer(0); + + //Initialize region without margins + const typename RegionType::IndexType innerIndex({m_Tile.region.GetIndex()[0]+ m_Tile.margin[3],m_Tile.region.GetIndex()[1]+m_Tile.margin[0]}); + const typename RegionType::SizeType innerSize({m_Tile.region.GetSize()[0]-m_Tile.margin[3]-m_Tile.margin[1],m_Tile.region.GetSize()[1]-m_Tile.margin[0]-m_Tile.margin[2]}); + const RegionType innerRegion(innerIndex,innerSize); + const unsigned int nbComps = this->GetInput()->GetNumberOfComponentsPerPixel(); + std::unordered_set<LabelType> stableLabels; + //Calculate stable segments, depending on whether tile is touching image edge + const unsigned int topMargin = m_Tile.tileNeighbors[NBH_TOP] >= 0 ? 1 : 0; + const unsigned int rightMargin = m_Tile.tileNeighbors[NBH_RIGHT] >= 0 ? 1 : 0; + const unsigned int bottomMargin = m_Tile.tileNeighbors[NBH_BOTTOM] >= 0 ? 1 : 0; + const unsigned int leftMargin = m_Tile.tileNeighbors[NBH_LEFT] >= 0 ? 1 : 0; + const bool stableSegments = (topMargin || rightMargin || bottomMargin || leftMargin) && (m_Margin > 0); + //Calculate margin region (contains all pixels except for ones on the edge of the image) + //TODO Improve this with faces calculator ? + const typename RegionType::IndexType marginIndex({m_Tile.region.GetIndex()[0]+leftMargin,m_Tile.region.GetIndex()[1]+topMargin}); + const typename RegionType::SizeType marginSize({m_Tile.region.GetSize()[0]-leftMargin-rightMargin,m_Tile.region.GetSize()[1]-topMargin-bottomMargin}); + const RegionType marginRegion(marginIndex,marginSize); + + typename RegionType::IndexType cornerIndex; + std::vector<RegionType> corners; + + if(bottomMargin && leftMargin) + { + const long unsigned int m = m_Tile.margin[2]; + cornerIndex[0]=m_Tile.region.GetIndex()[0]; + cornerIndex[1]=m_Tile.region.GetIndex()[1]+m_Tile.region.GetSize()[1]-m; + const typename RegionType::SizeType cornerSize({m,m}); + corners.push_back(RegionType(cornerIndex,cornerSize)); + } + if(bottomMargin && rightMargin) + { + const long unsigned int m = m_Tile.margin[2]; + cornerIndex[0]=m_Tile.region.GetIndex()[0]+m_Tile.region.GetSize()[0]-m; + cornerIndex[1]=m_Tile.region.GetIndex()[1]+m_Tile.region.GetSize()[1]-m; + const typename RegionType::SizeType cornerSize({m,m}); + corners.push_back(RegionType(cornerIndex,cornerSize)); + } + + //Write margin values into output + for(auto const& im : m_InputSeg) + { + itk::ImageRegionIterator<TOutputLabelImage> outputIt(this->GetOutput(),im->GetLargestPossibleRegion()); + itk::ImageRegionConstIterator<TOutputLabelImage> inputIt(im,im->GetLargestPossibleRegion()); + for(inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !outputIt.IsAtEnd(); ++inputIt, ++outputIt) + { + const LabelType currentLabel = inputIt.Get(); + outputIt.Set(currentLabel); + } + } + ListSampleType initialSeedMeans; + CountContainerType initialCount; + // //To avoid rehashes + initialSeedMeans.reserve(m_Tile.region.GetSize()[0]*m_Tile.region.GetSize()[1]/m_SpatialWidth/m_SpatialWidth); + initialCount.reserve(m_Tile.region.GetSize()[0]*m_Tile.region.GetSize()[1]/m_SpatialWidth/m_SpatialWidth); + //Update means and count in margin zones according to initial grid and stable elements + itk::ImageRegionIterator<TOutputLabelImage> outputIt(this->GetOutput(),this->GetOutput()->GetLargestPossibleRegion()); + itk::ImageRegionConstIteratorWithIndex<TInputImage> vit(this->GetInput(), this->GetInput()->GetLargestPossibleRegion()); + for(vit.GoToBegin(), outputIt.GoToBegin(); !outputIt.IsAtEnd() && !vit.IsAtEnd() ; ++outputIt, ++vit) + { + if(!inImageCorner(corners,vit.GetIndex())){ + LabelType currentLabel; + if(innerRegion.IsInside(vit.GetIndex())){ + //Set grid in inner region + currentLabel = vcl_floor(vit.GetIndex()[0]/m_SpatialWidth)+m_NbSPx*vcl_floor(vit.GetIndex()[1]/m_SpatialWidth)+1; + outputIt.Set(currentLabel); + } + else { + //Read from margin values + currentLabel = outputIt.Get(); + } + //Calculate initial means and count + if(initialSeedMeans.find(currentLabel) == initialSeedMeans.end()){ + initialCount.insert({currentLabel,1}); + + SampleType initialSeed(nbComps+2); + for (unsigned int i = 0; i < nbComps; i++) { + initialSeed[i] = vit.Get()[i]; + } + initialSeed[nbComps] = vit.GetIndex()[0]; + initialSeed[nbComps+1] = vit.GetIndex()[1]; + + initialSeedMeans.insert({currentLabel,initialSeed}); + } + else { + addToCentroid(vit.Get(),vit.GetIndex(),currentLabel,initialCount,initialSeedMeans); + } + //Calculate stable segments + if(stableSegments && !marginRegion.IsInside(vit.GetIndex()) && stableLabels.find(currentLabel) == stableLabels.end()){ + stableLabels.insert(currentLabel); + } + } + } + //Setup first iteration + double residual = 0; + bool firstLoop = (m_MaxIterationNumber > 0); + unsigned int iteration = 0; + //Copy initial values + ListSampleType currentSeedMeans(initialSeedMeans); + CountContainerType currentCount(initialCount); + simplePointCalculator<NeighborhoodContainerIteratorType,LabelType> spc; + + //Take neighborhood iterator on labels + const typename NeighborhoodLabelIteratorType::RadiusType radius = {{1,1}}; + NeighborhoodLabelIteratorType labIt(radius,this->GetOutput(),m_Tile.region); + itk::ImageRegionConstIteratorWithIndex<VectorImageType> vitRegion(this->GetInput(),m_Tile.region); + + while(firstLoop || (residual > m_Threshold && iteration < m_MaxIterationNumber)) + { + std::cout<<"Iteration "<<iteration<<", starting optimisation ..."<<std::endl; + for(vitRegion.GoToBegin(),labIt.GoToBegin();!vitRegion.IsAtEnd() && !labIt.IsAtEnd();++vitRegion,++labIt) + { + + if(!inImageCorner(corners,vitRegion.GetIndex())) + { + const LabelType currentLabel = labIt.GetCenterPixel(); + if(currentCount[currentLabel] > 1) + { + if(!stableSegments || stableLabels.find(currentLabel) == stableLabels.end()) + { + //Copy 4 connected unique labels into std::vector + NeighborhoodLabelsType neighborLabels; + const NeighborhoodType n = labIt.GetNeighborhood(); + typename NeighborhoodType::ConstIterator ni; + for(ni = n.Begin()+1 ;ni != n.End(); ni=ni+2) + { + + if(*ni != 0 && (!stableSegments || stableLabels.find(*ni) == stableLabels.end())) + { + if(std::find(neighborLabels.begin(), neighborLabels.end(), *ni) == neighborLabels.end()) + { + + neighborLabels.push_back(*ni); + } + } + } + int iMin=-1; + double distanceMin = std::numeric_limits<double>::max(); + if(neighborLabels.size() > 1) + { + bool topo_check = true; + //Point must be simple wrt all the 4 connected labeling neighbors + const unsigned int neighborLabelsSize = neighborLabels.size(); + for(unsigned int i = 0; i < neighborLabelsSize; i++) + { + topo_check&=spc.isSimpleLabel(n.Begin(), n.End(), neighborLabels[i]); + } + if(topo_check) + { + //Possible label change + + for(unsigned int i = 0; i < neighborLabelsSize; i++) + { + //Find closest label + double spectralDistance = 0; + double spatialDistance = 0; + const SampleType currentSeed = currentSeedMeans[neighborLabels[i]]; + for(unsigned int comp = 0; comp<nbComps;++comp) + { + spectralDistance+=(vitRegion.Get()[comp]-currentSeed[comp])*(vitRegion.Get()[comp]-currentSeed[comp]); + } + spatialDistance += (vitRegion.GetIndex()[0]-currentSeed[nbComps])*(vitRegion.GetIndex()[0]-currentSeed[nbComps]); + spatialDistance += (vitRegion.GetIndex()[1]-currentSeed[nbComps+1])*(vitRegion.GetIndex()[1]-currentSeed[nbComps+1]); + const double distance = vcl_sqrt(spectralDistance)/nbComps + m_SpatialDistanceWeight * vcl_sqrt(spatialDistance)/2; + if (distance < distanceMin) + { + iMin=i; + distanceMin=distance; + } + } + } + } + if (iMin >= 0 && neighborLabels[iMin] != currentLabel) + { + //Change label + const LabelType newLabel = neighborLabels[iMin]; + labIt.SetCenterPixel(newLabel); + + //Update seeds + //Remove point at currentlabel from means + removeFromCentroid(vitRegion.Get(),vitRegion.GetIndex(),currentLabel,currentCount,currentSeedMeans); + //neighborLabel[iMin] + //Add point at newLabel in means + addToCentroid(vitRegion.Get(),vitRegion.GetIndex(),newLabel,currentCount,currentSeedMeans); + } + } + } + } + } + //Compute residual + residual = 0; + //Update initialSeedMeans + typename ListSampleType::const_iterator newSeed = currentSeedMeans.begin(); + typename ListSampleType::iterator oldSeed = initialSeedMeans.begin(); + for(;newSeed != currentSeedMeans.end() && oldSeed != initialSeedMeans.end();++newSeed,++oldSeed) + { + //spectral residual + for(unsigned int i = 0; i < nbComps ;++i) + { + residual+=vcl_abs(newSeed->second[i] - oldSeed->second[i])/nbComps; + } + //spatial residual + for(unsigned int i = nbComps; i < nbComps+2 ;++i) + { + residual+=vcl_abs(newSeed->second[i] - oldSeed->second[i])/2; + } + + //Update initialSeedMeans + oldSeed->second=newSeed->second; + } + residual/=currentSeedMeans.size(); + std::cout<<"Done. Residual: "<<residual<<", max remaining iterations: "<<m_MaxIterationNumber-iteration<<std::endl; + + //Update count + typename CountContainerType::const_iterator newSeedCount = currentCount.begin(); + typename CountContainerType::iterator oldSeedCount = initialCount.begin(); + for(;newSeedCount != currentCount.end() && oldSeedCount != initialCount.end(); ++newSeedCount,++oldSeedCount) + { + oldSeedCount->second = newSeedCount->second; + } + firstLoop = false; + ++iteration; + + } + + return; + } + + +} //end namespace otb +#endif + diff --git a/include/otbSLICFilter.txx~ b/include/otbSLICFilter.txx~ new file mode 100644 index 0000000000000000000000000000000000000000..fe2e1d8d5e0e8cfbd165864e53d806360c2af907 --- /dev/null +++ b/include/otbSLICFilter.txx~ @@ -0,0 +1,383 @@ +#ifndef otbSLICFilter_txx +#define otbSLICFilter_txx + +#include "otbSLICFilter.h" + +namespace otb{ + + template <class TInputImage, class TOutputLabelImage> + SLICFilter<TInputImage, TOutputLabelImage> + ::SLICFilter() : + m_SpatialWidth(100), + m_SpatialDistanceWeight(1), + m_MaxIterationNumber(10), + m_Threshold(0.1), + m_NbSPx(1), + m_Margin(0) + { + this->SetNumberOfRequiredInputs(1); + this->SetNumberOfRequiredOutputs(1); + this->SetNthOutput(0,TOutputLabelImage::New()); + } + + template <class TInputImage, class TOutputLabelImage> + SLICFilter<TInputImage, TOutputLabelImage> + ::~SLICFilter() + {} + + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::SetInput(const TInputImage* input) + { + this->ProcessObject::SetNthInput(0,const_cast<TInputImage*>(input)); + } + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::AddInputMargin(typename TOutputLabelImage::Pointer InputSegPtr) + { + m_InputSeg.push_back(InputSegPtr); + } + + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::SetTile(ProcessingTile const& _t) + { + m_Tile = _t; + } + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::ClearMargins() + { + m_InputSeg.clear(); + } + template <class TInputImage, class TOutputLabelImage> + TInputImage * + SLICFilter<TInputImage, TOutputLabelImage> + ::GetInput() + { + return static_cast<TInputImage *>(this->itk::ProcessObject::GetInput(0)); + } + template <class TInputImage, class TOutputLabelImage> + TOutputLabelImage * + SLICFilter<TInputImage, TOutputLabelImage> + ::GetOutput() + { + return static_cast<TOutputLabelImage *>(this->itk::ProcessObject::GetOutput(0)); + } + template <class TInputImage, class TOutputLabelImage> + const TOutputLabelImage * + SLICFilter<TInputImage, TOutputLabelImage> + ::GetOutput() const + { + return static_cast<TOutputLabelImage *>(this->itk::ProcessObject::GetOutput(0)); + } + + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::GenerateInputRequestedRegion(){ + this->GetOutput()->SetRegions(this->GetInput()->GetLargestPossibleRegion()); + } + template <class TInputImage, class TOutputLabelImage> + bool + SLICFilter<TInputImage, TOutputLabelImage> + ::inImageCorner(std::vector<RegionType> const& corners, typename TOutputLabelImage::IndexType index){ + for(RegionType cornerRegion : corners) + if(cornerRegion.IsInside(index)) + return true; + return false; + } + + + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::removeFromCentroid(PixelType const& currentPixel, typename TInputImage::IndexType index, LabelType label, CountContainerType & count, ListSampleType & means){ + const typename CountContainerType::iterator searchCount = count.find(label); + //Update with running formulas + //Update count + searchCount->second--; + //Update Means + const typename ListSampleType::iterator searchM = means.find(label); + const SampleType oldMeans = searchM->second; + SampleType newMeans = oldMeans; + const double N = searchCount->second;//Cast int to double + const unsigned int nbComps = newMeans.size() - 2 ; + for(unsigned int i = 0 ; i < nbComps ; ++i) + { + newMeans[i] += (oldMeans[i]-currentPixel[i])/N; + } + newMeans[nbComps] += (oldMeans[nbComps]-index[0])/N; + newMeans[nbComps+1] += (oldMeans[nbComps+1]-index[1])/N; + searchM->second = newMeans; + } + + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::addToCentroid(PixelType const& currentPixel, typename TInputImage::IndexType index, LabelType label, CountContainerType & count, ListSampleType & means){ + const typename CountContainerType::iterator searchCount = count.find(label); + //Update with running formulas + //Update count + searchCount->second++; + //Update Means + const typename ListSampleType::iterator searchM = means.find(label); + const SampleType oldMeans = searchM->second; + SampleType newMeans = oldMeans; + const double N = searchCount->second;//Cast int to double + const unsigned int nbComps = newMeans.size() - 2 ; + for(unsigned int i = 0 ; i < nbComps ; ++i) + { + newMeans[i] += (currentPixel[i]-oldMeans[i])/N; + } + newMeans[nbComps] += (index[0]-oldMeans[nbComps])/N; + newMeans[nbComps+1] += (index[1]-oldMeans[nbComps+1])/N; + searchM->second = newMeans; + } + + template <class TInputImage, class TOutputLabelImage> + void + SLICFilter<TInputImage, TOutputLabelImage> + ::GenerateData() + { + this->GetOutput()->SetRegions(this->GetInput()->GetLargestPossibleRegion()); + this->GetOutput()->SetOrigin(this->GetInput()->GetOrigin()); + this->GetOutput()->SetSignedSpacing(this->GetInput()->GetSignedSpacing()); + this->GetOutput()->Allocate(); + this->GetOutput()->FillBuffer(0); + + //Initialize region without margins + const typename RegionType::IndexType innerIndex({m_Tile.region.GetIndex()[0]+ m_Tile.margin[3],m_Tile.region.GetIndex()[1]+m_Tile.margin[0]}); + const typename RegionType::SizeType innerSize({m_Tile.region.GetSize()[0]-m_Tile.margin[3]-m_Tile.margin[1],m_Tile.region.GetSize()[1]-m_Tile.margin[0]-m_Tile.margin[2]}); + const RegionType innerRegion(innerIndex,innerSize); + const unsigned int nbComps = this->GetInput()->GetNumberOfComponentsPerPixel(); + std::unordered_set<LabelType> stableLabels; + //Calculate stable segments, depending on whether tile is touching image edge + const unsigned int topMargin = m_Tile.tileNeighbors[NBH_TOP] >= 0 ? 1 : 0; + const unsigned int rightMargin = m_Tile.tileNeighbors[NBH_RIGHT] >= 0 ? 1 : 0; + const unsigned int bottomMargin = m_Tile.tileNeighbors[NBH_BOTTOM] >= 0 ? 1 : 0; + const unsigned int leftMargin = m_Tile.tileNeighbors[NBH_LEFT] >= 0 ? 1 : 0; + const bool stableSegments = (topMargin || rightMargin || bottomMargin || leftMargin) && (m_Margin > 0); + //Calculate margin region (contains all pixels except for ones on the edge of the image) + //TODO Improve this with faces calculator ? + const typename RegionType::IndexType marginIndex({m_Tile.region.GetIndex()[0]+leftMargin,m_Tile.region.GetIndex()[1]+topMargin}); + const typename RegionType::SizeType marginSize({m_Tile.region.GetSize()[0]-leftMargin-rightMargin,m_Tile.region.GetSize()[1]-topMargin-bottomMargin}); + const RegionType marginRegion(marginIndex,marginSize); + + typename RegionType::IndexType cornerIndex; + std::vector<RegionType> corners; + + if(bottomMargin && leftMargin) + { + const long unsigned int m = m_Tile.margin[2]; + cornerIndex[0]=m_Tile.region.GetIndex()[0]; + cornerIndex[1]=m_Tile.region.GetIndex()[1]+m_Tile.region.GetSize()[1]-m; + const typename RegionType::SizeType cornerSize({m,m}); + corners.push_back(RegionType(cornerIndex,cornerSize)); + } + if(bottomMargin && rightMargin) + { + const long unsigned int m = m_Tile.margin[2]; + cornerIndex[0]=m_Tile.region.GetIndex()[0]+m_Tile.region.GetSize()[0]-m; + cornerIndex[1]=m_Tile.region.GetIndex()[1]+m_Tile.region.GetSize()[1]-m; + const typename RegionType::SizeType cornerSize({m,m}); + corners.push_back(RegionType(cornerIndex,cornerSize)); + } + + //Write margin values into output + for(auto const& im : m_InputSeg) + { + itk::ImageRegionIterator<TOutputLabelImage> outputIt(this->GetOutput(),im->GetLargestPossibleRegion()); + itk::ImageRegionConstIterator<TOutputLabelImage> inputIt(im,im->GetLargestPossibleRegion()); + for(inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !outputIt.IsAtEnd(); ++inputIt, ++outputIt) + { + const LabelType currentLabel = inputIt.Get(); + outputIt.Set(currentLabel); + } + } + ListSampleType initialSeedMeans; + CountContainerType initialCount; + // //To avoid rehashes + initialSeedMeans.reserve(m_Tile.region.GetSize()[0]*m_Tile.region.GetSize()[1]/m_SpatialWidth/m_SpatialWidth); + initialCount.reserve(m_Tile.region.GetSize()[0]*m_Tile.region.GetSize()[1]/m_SpatialWidth/m_SpatialWidth); + //Update means and count in margin zones according to initial grid and stable elements + itk::ImageRegionIterator<TOutputLabelImage> outputIt(this->GetOutput(),this->GetOutput()->GetLargestPossibleRegion()); + itk::ImageRegionConstIteratorWithIndex<TInputImage> vit(this->GetInput(), this->GetInput()->GetLargestPossibleRegion()); + for(vit.GoToBegin(), outputIt.GoToBegin(); !outputIt.IsAtEnd() && !vit.IsAtEnd() ; ++outputIt, ++vit) + { + if(!inImageCorner(corners,vit.GetIndex())){ + LabelType currentLabel; + if(innerRegion.IsInside(vit.GetIndex())){ + //Set grid in inner region + currentLabel = vcl_floor(vit.GetIndex()[0]/m_SpatialWidth)+m_NbSPx*vcl_floor(vit.GetIndex()[1]/m_SpatialWidth)+1; + outputIt.Set(currentLabel); + } + else { + //Read from margin values + currentLabel = outputIt.Get(); + } + //Calculate initial means and count + if(initialSeedMeans.find(currentLabel) == initialSeedMeans.end()){ + initialCount.insert({currentLabel,1}); + + SampleType initialSeed(nbComps+2); + for (unsigned int i = 0; i < nbComps; i++) { + initialSeed[i] = vit.Get()[i]; + } + initialSeed[nbComps] = vit.GetIndex()[0]; + initialSeed[nbComps+1] = vit.GetIndex()[1]; + + initialSeedMeans.insert({currentLabel,initialSeed}); + } + else { + addToCentroid(vit.Get(),vit.GetIndex(),currentLabel,initialCount,initialSeedMeans); + } + //Calculate stable segments + if(stableSegments && !marginRegion.IsInside(vit.GetIndex()) && stableLabels.find(currentLabel) == stableLabels.end()){ + stableLabels.insert(currentLabel); + } + } + } + //Setup first iteration + double residual = 0; + bool firstLoop = (m_MaxIterationNumber > 0); + unsigned int iteration = 0; + //Copy initial values + ListSampleType currentSeedMeans(initialSeedMeans); + CountContainerType currentCount(initialCount); + simplePointCalculator<NeighborhoodContainerIteratorType,LabelType> spc; + + //Take neighborhood iterator on labels + const typename NeighborhoodLabelIteratorType::RadiusType radius = {{1,1}}; + NeighborhoodLabelIteratorType labIt(radius,this->GetOutput(),m_Tile.region); + itk::ImageRegionConstIteratorWithIndex<VectorImageType> vitRegion(this->GetInput(),m_Tile.region); + + while(firstLoop || (residual > m_Threshold && iteration < m_MaxIterationNumber)) + { + std::cout<<"Iteration "<<iteration<<", starting optimisation ..."<<std::endl; + for(vitRegion.GoToBegin(),labIt.GoToBegin();!vitRegion.IsAtEnd() && !labIt.IsAtEnd();++vitRegion,++labIt) + { + + if(!inImageCorner(corners,vitRegion.GetIndex())) + { + const LabelType currentLabel = labIt.GetCenterPixel(); + if(currentCount[currentLabel] > 1) + { + if(!stableSegments || stableLabels.find(currentLabel) == stableLabels.end()) + { + //Copy 4 connected unique labels into std::vector + NeighborhoodLabelsType neighborLabels; + const NeighborhoodType n = labIt.GetNeighborhood(); + typename NeighborhoodType::ConstIterator ni; + for(ni = n.Begin()+1 ;ni != n.End(); ni=ni+2) + { + + if(*ni != 0 && (!stableSegments || stableLabels.find(*ni) == stableLabels.end())) + { + if(std::find(neighborLabels.begin(), neighborLabels.end(), *ni) == neighborLabels.end()) + { + + neighborLabels.push_back(*ni); + } + } + } + int iMin=-1; + double distanceMin = std::numeric_limits<double>::max(); + if(neighborLabels.size() > 1) + { + bool topo_check = true; + //Point must be simple wrt all the 4 connected labeling neighbors + const unsigned int neighborLabelsSize = neighborLabels.size(); + for(unsigned int i = 0; i < neighborLabelsSize; i++) + { + topo_check&=spc.isSimpleLabel(n.Begin(), n.End(), neighborLabels[i]); + } + if(topo_check) + { + //Possible label change + + for(unsigned int i = 0; i < neighborLabelsSize; i++) + { + //Find closest label + double spectralDistance = 0; + double spatialDistance = 0; + const SampleType currentSeed = currentSeedMeans[neighborLabels[i]]; + for(unsigned int comp = 0; comp<nbComps;++comp) + { + spectralDistance+=(vitRegion.Get()[comp]-currentSeed[comp])*(vitRegion.Get()[comp]-currentSeed[comp]); + } + spatialDistance += (vitRegion.GetIndex()[0]-currentSeed[nbComps])*(vitRegion.GetIndex()[0]-currentSeed[nbComps]); + spatialDistance += (vitRegion.GetIndex()[1]-currentSeed[nbComps+1])*(vitRegion.GetIndex()[1]-currentSeed[nbComps+1]); + const double distance = vcl_sqrt(spectralDistance)/nbComps + m_SpatialDistanceWeight * vcl_sqrt(spatialDistance)/2; + if (distance < distanceMin) + { + iMin=i; + distanceMin=distance; + } + } + } + } + if (iMin >= 0 && neighborLabels[iMin] != currentLabel) + { + //Change label + const LabelType newLabel = neighborLabels[iMin]; + labIt.SetCenterPixel(newLabel); + + //Update seeds + //Remove point at currentlabel from means + removeFromCentroid(vitRegion.Get(),vitRegion.GetIndex(),currentLabel,currentCount,currentSeedMeans); + //neighborLabel[iMin] + //Add point at newLabel in means + addToCentroid(vitRegion.Get(),vitRegion.GetIndex(),newLabel,currentCount,currentSeedMeans); + } + } + } + } + } + + residual = 0; + //Update initialSeedMeans + typename ListSampleType::const_iterator newSeed = currentSeedMeans.begin(); + typename ListSampleType::iterator oldSeed = initialSeedMeans.begin(); + for(;newSeed != currentSeedMeans.end() && oldSeed != initialSeedMeans.end();++newSeed,++oldSeed) + { + //spectral residual + for(unsigned int i = 0; i < nbComps ;++i) + { + residual+=vcl_abs(newSeed->second[i] - oldSeed->second[i])/nbComps; + } + //spatial residual + for(unsigned int i = nbComps; i < nbComps+2 ;++i) + { + residual+=vcl_abs(newSeed->second[i] - oldSeed->second[i])/2; + } + + //Update initialSeedMeans + oldSeed->second=newSeed->second; + } + residual/=currentSeedMeans.size(); + std::cout<<"Done. Residual: "<<residual<<", max remaining iterations: "<<m_MaxIterationNumber-iteration<<std::endl; + + //Update count + typename CountContainerType::const_iterator newSeedCount = currentCount.begin(); + typename CountContainerType::iterator oldSeedCount = initialCount.begin(); + for(;newSeedCount != currentCount.end() && oldSeedCount != initialCount.end(); ++newSeedCount,++oldSeedCount) + { + oldSeedCount->second = newSeedCount->second; + } + firstLoop = false; + ++iteration; + + } + + return; + } + + +} //end namespace otb +#endif + diff --git a/include/otbSLICScheduler.h b/include/otbSLICScheduler.h new file mode 100644 index 0000000000000000000000000000000000000000..0220507982cf19d458175a594209ee2830f2c677 --- /dev/null +++ b/include/otbSLICScheduler.h @@ -0,0 +1,150 @@ +#ifndef otbSLICScheduler_h +#define otbSLICScheduler_h + +#include "itkMacro.h" +//OTB, ITK includes +#include "otbVectorImage.h" +#include "otbImage.h" +#include "otbImageFileWriter.h" +#include "otbImageFileReader.h" + +#include "itkImageRegionConstIteratorWithIndex.h" +#include "itkImageRegionIteratorWithIndex.h" +#include "itkImageRegionExclusionConstIteratorWithIndex.h" + +#include "itkImageToImageFilter.h" +#include "itkNeighborhoodIterator.h" +#include "itkExtractImageFilter.h" +#include "otbMPIConfig.h" + +//Own includes +#include "otbSLICFilter.h" + +#include <itksys/SystemTools.hxx> + +#include <gdal.h> +#include <gdal_priv.h> +#if defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wshadow" +#include <vrtdataset.h> +# pragma GCC diagnostic pop +#else +#include <vrtdataset.h> +#endif + +#include <ogr_spatialref.h> + + + + +namespace otb{ + + template <class TInputImage, class TOutputLabelImage> + class ITK_EXPORT SLICScheduler : public itk::Object + { + public: + typedef SLICScheduler Self; + typedef itk::Object 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(SLICScheduler, ImageToImageFilter); + + /** ImageDimension constants */ + itkStaticConstMacro(ImageDimension, unsigned int, TInputImage::ImageDimension); + + typedef double RealType; + typedef typename TOutputLabelImage::PixelType LabelType; + typedef otb::ImageFileReader<TInputImage> ReaderType; + typedef TOutputLabelImage OutputLabelImageType; + typedef TInputImage VectorImageType; + typedef typename TInputImage::RegionType RegionType; + typedef typename std::vector<RealType> SampleType; + typedef std::vector<SampleType> ListSampleType; + typedef std::vector<LabelType> NeighborhoodLabelsType; + typedef itk::ConstantBoundaryCondition<TOutputLabelImage> BoundaryConditionType; + typedef itk::NeighborhoodIterator<TOutputLabelImage,BoundaryConditionType> NeighborhoodLabelIteratorType; + typedef typename NeighborhoodLabelIteratorType::NeighborhoodType NeighborhoodType; + typedef typename NeighborhoodType::ConstIterator NeighborhoodContainerIteratorType; + typedef otb::ImageFileWriter<TOutputLabelImage> WriterType; + typedef std::vector<otb::ProcessingTile> TileListType; + typedef otb::SLICFilter<TInputImage, TOutputLabelImage> SLICFilter; + typedef itk::ExtractImageFilter<TInputImage,TInputImage> ExtractFilterType; + typedef otb::ImageFileReader<TOutputLabelImage> LabelReaderType; + typedef itk::ExtractImageFilter<TOutputLabelImage, TOutputLabelImage> LabelExtractFilterType; + + // Sets the spatial width, i.e. average root of size of the output superpixels + itkSetMacro(SpatialWidth,unsigned int); + + // Sets the spatial distance weight, i.e. the compacity/homogeneity ratio of SP + itkSetMacro(SpatialDistanceWeight,RealType); + + //Sets the max number of iterations + itkSetMacro(MaxIterationNumber,unsigned int); + + //Sets the residual threshold for stopping the algorithm + itkSetMacro(Threshold,RealType); + + //Number of tiles in X direction + itkSetMacro(NbTilesX,unsigned int); + + //Number of tiles in Y direction + itkSetMacro(NbTilesY,unsigned int); + + //Margin (in SP widths) + itkSetMacro(Margin,RealType); + + //Prefix for file writing + itkSetMacro(Prefix,std::string); + + //Name of the input for writing temp files + itkSetMacro(InputName,std::string); + + //Calculate a list of regions of the input image with the appropriate margins + static std::vector<ProcessingTile> SplitOTBImage(const TInputImage * imagePtr, // input image + const unsigned int tileWidth, // width of the tile + const unsigned int tileHeight, // height of the tile + const unsigned int margin, // stability margin + const unsigned int nbTilesX, + const unsigned int nbTilesY, + const std::string temporaryFilesPrefix); + + //Generate the .vrt corresponding to a list of input tiles + static void writeVRTRegions(TileListType const& inputRegions, GDALDataset *VRTOutput, std::string prefix, unsigned int band); + + //Apply SLIC segmentation to the input image + void Run(); + + void SetInputImage(typename TInputImage::Pointer InputImagePointer); + + protected: + SLICScheduler(); + + ~SLICScheduler() ITK_OVERRIDE; + + private: + SLICScheduler(const Self &); //purposely not implemented + void operator =(const Self&); //purposely not implemented + unsigned int m_SpatialWidth; + RealType m_SpatialDistanceWeight; + unsigned int m_MaxIterationNumber; + RealType m_Threshold; + std::string m_Prefix; + std::string m_InputName; + ListSampleType m_SeedMeans; + unsigned int m_NbTilesX; + unsigned int m_NbTilesY; + RealType m_Margin; + typename TInputImage::Pointer m_InputImage; + }; +} +#ifndef OTB_MANUAL_INSTANTIATION +#include "otbSLICScheduler.txx" +#endif + +#endif diff --git a/include/otbSLICScheduler.h~ b/include/otbSLICScheduler.h~ new file mode 100644 index 0000000000000000000000000000000000000000..51b8eadbdf4b7abe33b92033a116679d4721ff22 --- /dev/null +++ b/include/otbSLICScheduler.h~ @@ -0,0 +1,147 @@ +#ifndef otbSLICScheduler_h +#define otbSLICScheduler_h + +#include "itkMacro.h" +//OTB, ITK includes +#include "otbVectorImage.h" +#include "otbImage.h" +#include "otbImageFileWriter.h" +#include "otbImageFileReader.h" + +#include "itkImageRegionConstIteratorWithIndex.h" +#include "itkImageRegionIteratorWithIndex.h" +#include "itkImageRegionExclusionConstIteratorWithIndex.h" + +#include "itkImageToImageFilter.h" +#include "itkNeighborhoodIterator.h" +#include "itkExtractImageFilter.h" +#include "otbMPIConfig.h" + +//Own includes +#include "otbSLICScheduler.h" + +#include <itksys/SystemTools.hxx> + +#include <gdal.h> +#include <gdal_priv.h> +#if defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wshadow" +#include <vrtdataset.h> +# pragma GCC diagnostic pop +#else +#include <vrtdataset.h> +#endif + +#include <ogr_spatialref.h> + + + + +namespace otb{ + + template <class TInputImage, class TOutputLabelImage> + class ITK_EXPORT SLICScheduler : public itk::Object + { + public: + typedef SLICScheduler Self; + typedef itk::Object 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(SLICScheduler, ImageToImageFilter); + + /** ImageDimension constants */ + itkStaticConstMacro(ImageDimension, unsigned int, TInputImage::ImageDimension); + + typedef double RealType; + typedef typename TOutputLabelImage::PixelType LabelType; + typedef TOutputLabelImage OutputLabelImageType; + typedef TInputImage VectorImageType; + typedef typename TInputImage::RegionType RegionType; + + typedef typename std::vector<RealType> SampleType; + typedef std::vector<SampleType> ListSampleType; + + typedef std::vector<LabelType> NeighborhoodLabelsType; + typedef itk::ConstantBoundaryCondition<TOutputLabelImage> BoundaryConditionType; + typedef itk::NeighborhoodIterator<TOutputLabelImage,BoundaryConditionType> NeighborhoodLabelIteratorType; + typedef typename NeighborhoodLabelIteratorType::NeighborhoodType NeighborhoodType; + typedef typename NeighborhoodType::ConstIterator NeighborhoodContainerIteratorType; + + + typedef otb::ImageFileWriter<TOutputLabelImage> WriterType; + + typedef std::vector<otb::ProcessingTile> TileListType; + + // Sets the spatial width, i.e. average root of size of the output superpixels + itkSetMacro(SpatialWidth,unsigned int); + + // Sets the spatial distance weight, i.e. the compacity/homogeneity ratio of SP + itkSetMacro(SpatialDistanceWeight,RealType); + + //Sets the max number of iterations + itkSetMacro(MaxIterationNumber,unsigned int); + + //Sets the residual threshold for stopping the algorithm + itkSetMacro(Threshold,RealType); + + //Number of tiles in X direction + itkSetMacro(NbTilesX,unsigned int); + + //Number of tiles in Y direction + itkSetMacro(NbTilesY,unsigned int); + + //Margin (in SP widths) + itkSetMacro(Margin,RealType); + + //Prefix for file writing + itkSetMacro(Prefix,std::string); + + //Test + itkSetMacro(InputName,std::string); + + static std::vector<ProcessingTile> SplitOTBImage(const TInputImage * imagePtr, // input image + const unsigned int tileWidth, // width of the tile + const unsigned int tileHeight, // height of the tile + const unsigned int margin, // stability margin + const unsigned int nbTilesX, + const unsigned int nbTilesY, + const std::string temporaryFilesPrefix); + + static void writeVRTRegions(TileListType const& inputRegions, GDALDataset *VRTOutput, std::string prefix, unsigned int band); + + void Run(); + + void SetInputImage(typename TInputImage::Pointer InputImagePointer); + + protected: + SLICScheduler(); + + ~SLICScheduler() ITK_OVERRIDE; + + private: + SLICScheduler(const Self &); //purposely not implemented + void operator =(const Self&); //purposely not implemented + unsigned int m_SpatialWidth; + RealType m_SpatialDistanceWeight; + unsigned int m_MaxIterationNumber; + RealType m_Threshold; + std::string m_Prefix; + std::string m_InputName; + ListSampleType m_SeedMeans; + unsigned int m_NbTilesX; + unsigned int m_NbTilesY; + RealType m_Margin; + typename TInputImage::Pointer m_InputImage; + }; +} +#ifndef OTB_MANUAL_INSTANTIATION +#include "otbSLICScheduler.txx" +#endif + +#endif diff --git a/include/otbSLICScheduler.txx b/include/otbSLICScheduler.txx new file mode 100644 index 0000000000000000000000000000000000000000..54e395d62feebf5e2ad2df6758d29fea1183e67c --- /dev/null +++ b/include/otbSLICScheduler.txx @@ -0,0 +1,553 @@ +#ifndef otbSLICScheduler_txx +#define otbSLICScheduler_txx + +#include "otbSLICScheduler.h" + +namespace otb{ + + template <class TInputImage, class TOutputLabelImage> + SLICScheduler<TInputImage, TOutputLabelImage> + ::SLICScheduler() : + m_SpatialWidth(100), + m_SpatialDistanceWeight(1), + m_MaxIterationNumber(10), + m_Threshold(0.1), + m_NbTilesX(1), + m_NbTilesY(1), + m_Margin(0) + {} + + template <class TInputImage, class TOutputLabelImage> + SLICScheduler<TInputImage, TOutputLabelImage> + ::~SLICScheduler() + {} + + template <class TInputImage, class TOutputLabelImage> + void + SLICScheduler<TInputImage, TOutputLabelImage> + ::SetInputImage(typename TInputImage::Pointer InputImagePointer) + { + m_InputImage = InputImagePointer; + } + + template <class TInputImage, class TOutputLabelImage> + void + SLICScheduler<TInputImage, TOutputLabelImage> + ::Run() + { + typename otb::MPIConfig::Pointer mpiConfig = otb::MPIConfig::Instance(); + unsigned int myRank = mpiConfig->GetMyRank(); + unsigned int nbProcs = mpiConfig->GetNbProcs(); + + const unsigned int imageSizeX = m_InputImage->GetLargestPossibleRegion().GetSize()[0]; + const unsigned int imageSizeY = m_InputImage->GetLargestPossibleRegion().GetSize()[1]; + + const unsigned int tileWidth = imageSizeX/m_NbTilesX; + const unsigned int tileHeight = imageSizeY/m_NbTilesY; + const unsigned int pixelMargin = m_Margin*m_SpatialWidth; + + typedef std::vector<otb::ProcessingTile> TileListType; + + //Tiles for processing with no margins + TileListType tilesNoMargin = SplitOTBImage(m_InputImage, tileWidth, tileHeight, 0, m_NbTilesX, m_NbTilesY, m_Prefix); + + //Tiles for processing with margins + TileListType tilesMargin = SplitOTBImage(m_InputImage, tileWidth, tileHeight, pixelMargin, m_NbTilesX, m_NbTilesY, m_Prefix); + + TileListType whiteTiles; + TileListType blackTiles; + TileListType tilesToWrite; + + //Generate list of black and white tiles to process + //Margin mode + if (m_Margin > 0) { + for(unsigned int y = 0 ; y < m_NbTilesY; ++y){ + for(unsigned int x = 0 ; x < m_NbTilesX; ++x){ + if(!((x+y)%2)){ //if x + y even + blackTiles.push_back(tilesNoMargin[x+m_NbTilesX*y]); + } + else{ + whiteTiles.push_back(tilesMargin[x+m_NbTilesX*y]); + } + } + } + if(myRank < blackTiles.size()) + { + //Dispatch according to rank of MPI process + unsigned int splitIdx = myRank; + while(splitIdx < blackTiles.size()) + { + tilesToWrite.push_back(blackTiles[splitIdx]); + splitIdx+=nbProcs; + } + } + } + //Simple streaming mode + else{ + if(myRank < tilesNoMargin.size()) + { + unsigned int splitIdx = myRank; + while(splitIdx < tilesNoMargin.size()) + { + tilesToWrite.push_back(tilesNoMargin[splitIdx]); + splitIdx+=nbProcs; + } + } + } + //First process the black tiles (no margins) + for (otb::ProcessingTile const& t : tilesToWrite) + { + typename ReaderType::Pointer reader = ReaderType::New(); + reader->SetFileName(m_InputName); + reader->SetReleaseDataFlag(true); + + typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); + extractFilter->SetReleaseDataFlag(true); + extractFilter->SetInput(reader->GetOutput()); + extractFilter->SetExtractionRegion(t.region); + + typename SLICFilter::Pointer filter = SLICFilter::New(); + filter->SetReleaseDataFlag(true); + filter->SetSpatialWidth(m_SpatialWidth); + filter->SetSpatialDistanceWeight(m_SpatialDistanceWeight); + filter->SetMaxIterationNumber(m_MaxIterationNumber); + filter->SetThreshold(m_Threshold); + filter->SetNbSPx(vcl_ceil((float) m_InputImage->GetLargestPossibleRegion().GetSize()[0]/m_SpatialWidth)); + filter->SetTile(t); + filter->SetMargin(m_Margin); + filter->SetInput(extractFilter->GetOutput()); + + typename WriterType::Pointer writer = WriterType::New(); + std::stringstream name; + name << m_Prefix << "_" << t.region.GetIndex()[0] << "_" << t.region.GetIndex()[1] << "_" << t.region.GetSize()[0] << "_" << t.region.GetSize()[1] << ".tif"; + writer->SetInput(filter->GetOutput()); + writer->SetFileName(name.str()); + writer->SetNumberOfDivisionsStrippedStreaming(1); + writer->Update(); + std::cout << myRank << " writing b region :" << name.str() << std::endl; + } + //Wait for all black regions to be processed + mpiConfig->barrier(); + + //Dispatch white tiles to different processes + if(m_Margin > 0){ + tilesToWrite.clear(); + for(unsigned int line = 0 ; line < m_NbTilesY ; line++){ + unsigned int nbWhite = vcl_floor(m_NbTilesX/2); + if (m_NbTilesX%2){ + nbWhite += line%2; + } + if(myRank < nbWhite) + { + unsigned int splitIdx = myRank; + while(splitIdx < nbWhite) + { + unsigned int ind; + if(m_NbTilesX%2) + ind = m_NbTilesX*vcl_floor(line/2) + splitIdx + vcl_floor(m_NbTilesX/2)*(line%2); + else + ind = splitIdx + nbWhite*line; + tilesToWrite.push_back(whiteTiles[ind]); + splitIdx+=nbProcs; + } + } + + //Process tiles + for (otb::ProcessingTile t : tilesToWrite) + { + typename ReaderType::Pointer reader = ReaderType::New(); + reader->SetFileName(m_InputName); + reader->SetReleaseDataFlag(true); + typename SLICFilter::Pointer filter = SLICFilter::New(); + filter->SetSpatialWidth(m_SpatialWidth); + filter->SetSpatialDistanceWeight(m_SpatialDistanceWeight); + filter->SetMaxIterationNumber(m_MaxIterationNumber); + filter->SetThreshold(m_Threshold); + filter->SetNbSPx(vcl_ceil((float) m_InputImage->GetLargestPossibleRegion().GetSize()[0]/m_SpatialWidth)); + filter->SetMargin(m_Margin); + typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); + extractFilter->SetReleaseDataFlag(true); + extractFilter->SetInput(reader->GetOutput()); + //Extract main region + extractFilter->SetExtractionRegion(t.region); + filter->SetTile(t); + filter->ClearMargins(); + filter->SetInput(extractFilter->GetOutput()); + filter->SetReleaseDataFlag(true); + //Read neighboring black tiles margin areas and load into filter + for (unsigned int i = 0; i < 8 ; i++){ + if(pixelMargin > 0 && t.tileNeighbors[i]>=0){ + typename TOutputLabelImage::RegionType marginRegion; + typename TOutputLabelImage::RegionType r; + bool read = false; + switch (i){ + case otb::NBH_TOP:{ + //Normal case + r = tilesNoMargin[t.tileNeighbors[i]].region; + // typename TOutputLabelImage::RegionType::IndexType index = r.GetIndex(); + typename TOutputLabelImage::RegionType::IndexType index({0,0}); + typename TOutputLabelImage::RegionType::SizeType size({r.GetSize()[0],pixelMargin}); + index[1]+=r.GetSize()[1]-pixelMargin; + //Accomodate for corners + if(t.tileNeighbors[otb::NBH_LEFT]>=0){ + //if left neighbor + index[0]+=pixelMargin; + size[0]-=pixelMargin; + + } + if(t.tileNeighbors[otb::NBH_RIGHT]>=0){ + //if right neighbor + size[0]-=pixelMargin; + } + marginRegion.SetIndex(index); + marginRegion.SetSize(size); + read = true; + break; + } + case otb::NBH_BOTTOM:{ + r = tilesNoMargin[t.tileNeighbors[i]].region; + // typename TOutputLabelImage::RegionType::IndexType index = r.GetIndex(); + typename TOutputLabelImage::RegionType::IndexType index({0,0}); + const typename TOutputLabelImage::RegionType::SizeType size({r.GetSize()[0],pixelMargin}); + marginRegion.SetIndex(index); + marginRegion.SetSize(size); + read = true; + break; + } + case otb::NBH_LEFT:{ + r = tilesNoMargin[t.tileNeighbors[i]].region; + // typename TOutputLabelImage::RegionType::IndexType index = r.GetIndex(); + typename TOutputLabelImage::RegionType::IndexType index({0,0}); + index[0]+=r.GetSize()[0]-pixelMargin; + typename TOutputLabelImage::RegionType::SizeType size({pixelMargin,r.GetSize()[1]}); + if(t.tileNeighbors[otb::NBH_TOP]>=0){ + //if right neighbor + index[1]+=pixelMargin; + size[1]-=pixelMargin; + } + marginRegion.SetIndex(index); + marginRegion.SetSize(size); + read = true; + break; + } + case otb::NBH_RIGHT:{ + r = tilesNoMargin[t.tileNeighbors[i]].region; + // typename TOutputLabelImage::RegionType::IndexType index = r.GetIndex(); + typename TOutputLabelImage::RegionType::IndexType index({0,0}); + typename TOutputLabelImage::RegionType::SizeType size({pixelMargin,r.GetSize()[1]}); + if(t.tileNeighbors[otb::NBH_TOP]>=0){ + //if right neighbor + index[1]+=pixelMargin; + size[1]-=pixelMargin; + } + marginRegion.SetIndex(index); + marginRegion.SetSize(size); + read = true; + break; + } + case otb::NBH_TOP_LEFT: { + r = tilesMargin[t.tileNeighbors[i]].region; + // typename TOutputLabelImage::RegionType::IndexType index = r.GetIndex(); + typename TOutputLabelImage::RegionType::IndexType index({0,0}); + index[0]+=r.GetSize()[0] - 2*pixelMargin; + index[1]+=r.GetSize()[1] - 2*pixelMargin; + const typename TOutputLabelImage::RegionType::SizeType size({2*pixelMargin,2*pixelMargin}); + marginRegion.SetIndex(index); + marginRegion.SetSize(size); + read = true; + break; + } + case otb::NBH_TOP_RIGHT: { + r = tilesMargin[t.tileNeighbors[i]].region; + // typename TOutputLabelImage::RegionType::IndexType index = r.GetIndex(); + typename TOutputLabelImage::RegionType::IndexType index({0,0}); + index[1]+=r.GetSize()[1] - 2*pixelMargin; + const typename TOutputLabelImage::RegionType::SizeType size({2*pixelMargin,2*pixelMargin}); + marginRegion.SetIndex(index); + marginRegion.SetSize(size); + read = true; + break; + } + } + if(read){ + typename LabelExtractFilterType::Pointer marginExtract = LabelExtractFilterType::New(); + std::stringstream readNeighborName; + readNeighborName << m_Prefix <<"_"<< r.GetIndex()[0] << "_" << r.GetIndex()[1] << "_"<< r.GetSize()[0] << "_"<< r.GetSize()[1] << ".tif"; + typename LabelReaderType::Pointer labelReader = LabelReaderType::New(); + labelReader->SetReleaseDataFlag(true); + labelReader->SetFileName(readNeighborName.str()); + //Set the index of the output (reader gives 0 0) + marginExtract->SetInput(labelReader->GetOutput()); + marginExtract->SetExtractionRegion(marginRegion); + marginExtract->Update(); + marginRegion.SetIndex({marginRegion.GetIndex()[0]+r.GetIndex()[0],marginRegion.GetIndex()[1]+r.GetIndex()[1]}); + marginExtract->GetOutput()->SetRegions(marginRegion); + filter->AddInputMargin(marginExtract->GetOutput()); + } + } + } + filter->Update(); + typename WriterType::Pointer writer = WriterType::New(); + writer->SetInput(filter->GetOutput()); + std::stringstream name; + name << m_Prefix << "_" << t.region.GetIndex()[0] << "_" << t.region.GetIndex()[1] << "_" << t.region.GetSize()[0] << "_" << t.region.GetSize()[1] << ".tif"; + writer->SetFileName(name.str()); + writer->SetNumberOfDivisionsStrippedStreaming(1); + writer->Update(); + std::cout << myRank << " writing w region :" << name.str() << std::endl; + } + tilesToWrite.clear(); + mpiConfig->barrier(); + } + } + try + { + if (myRank == 0) + { + // VRT Filename + std::stringstream vrtfOut; + vrtfOut<< m_Prefix << ".vrt"; + + // Data type + GDALDataType dataType; + dataType = GDALGetDataTypeByName("UInt32"); + + // Get VRT driver + GDALAllRegister(); + GDALDriver *driver = GetGDALDriverManager()->GetDriverByName("VRT"); + if (driver == NULL) { + mpiConfig->logError("Error opening VRT driver."); + mpiConfig->abort(EXIT_FAILURE); + } + + // Create output raster + GDALDataset *VRTOutput = driver->Create(vrtfOut.str().c_str(), + imageSizeX, + imageSizeY, + 0, + dataType, + NULL); // No options + if (VRTOutput == NULL) { + mpiConfig->logError("driver->Create call failed.\n"); + mpiConfig->abort(EXIT_FAILURE); + } + + // Set GeoTransform + double gt[6]; + gt[0] = m_InputImage->GetOrigin()[0] - 0.5*m_InputImage->GetSignedSpacing()[0]; + gt[1] = m_InputImage->GetSignedSpacing()[0]; + gt[2] = 0.0; + gt[3] = m_InputImage->GetOrigin()[1] - 0.5*m_InputImage->GetSignedSpacing()[1]; + gt[4] = 0.0; + gt[5] = m_InputImage->GetSignedSpacing()[1]; + VRTOutput->SetGeoTransform(gt); + + // Set projection + OGRSpatialReference out_sr; + char *wkt = NULL; + out_sr.SetFromUserInput(m_InputImage->GetProjectionRef().c_str()); + out_sr.exportToWkt(&wkt); + VRTOutput->SetProjection(wkt); + VRTOutput->AddBand(dataType, NULL); + if(m_Margin == 0){ + writeVRTRegions(tilesNoMargin, VRTOutput, m_Prefix, 1); + } + else{ + writeVRTRegions(blackTiles, VRTOutput, m_Prefix, 1); + writeVRTRegions(whiteTiles, VRTOutput, m_Prefix, 1); + } + // Close + OGRFree(wkt); + GDALClose(VRTOutput); + } + } + catch (itk::ExceptionObject& err) + { + std::stringstream message; + message << "ExceptionObject caught: " << err << std::endl; + mpiConfig->logError(message.str()); + mpiConfig->abort(EXIT_FAILURE); + } + } + template<class TInputImage, class TOutputLabelImage> + void + SLICScheduler<TInputImage,TOutputLabelImage> + ::writeVRTRegions(TileListType const& inputRegions, GDALDataset *VRTOutput, std::string prefix, unsigned int band){ + for(otb::ProcessingTile t : inputRegions) + { + int tileSizeX = t.region.GetSize()[0]; + int tileSizeY = t.region.GetSize()[1]; + int tileIndexX = t.region.GetIndex()[0]; + int tileIndexY = t.region.GetIndex()[1]; + std::stringstream tileFileName; + tileFileName <<prefix<<"_"<<tileIndexX<<"_"<<tileIndexY<<"_"<<tileSizeX<<"_"<<tileSizeY<<".tif"; + + GDALDataset *dataset = (GDALDataset*) GDALOpen(tileFileName.str().c_str(), GA_ReadOnly); + + VRTSourcedRasterBand *VRTBand = dynamic_cast<VRTSourcedRasterBand*> (VRTOutput->GetRasterBand(band)); + VRTBand->AddComplexSource(dataset->GetRasterBand(band), + 0,//Xoffset + 0,//Yoffset + tileSizeX,//Dimensions + tileSizeY,//Dimensions + tileIndexX,//Index + tileIndexY,//Index + tileSizeX,//Dimensions + tileSizeY,//Dimensions + 0.0,//Scale offset + 1,//Scale Ratio + 0);//NodataValue + } + } + template<class TInputImage, class TOutputLabelImage> + std::vector<ProcessingTile> + SLICScheduler<TInputImage,TOutputLabelImage> + ::SplitOTBImage(const TInputImage * imagePtr, // input image + const unsigned int tileWidth, // width of the tile + const unsigned int tileHeight, // height of the tile + const unsigned int margin, // stability margin + const unsigned int nbTilesX, + const unsigned int nbTilesY, + const std::string temporaryFilesPrefix) + { + std::vector<ProcessingTile> tiles; + // Image size + const unsigned int imageWidth = imagePtr->GetLargestPossibleRegion().GetSize()[0]; + const unsigned int imageHeight = imagePtr->GetLargestPossibleRegion().GetSize()[1]; + + /* Loop over the tiles*/ + tiles.assign(nbTilesX * nbTilesY, ProcessingTile()); + unsigned int i = 0; + for(unsigned int row = 0; row < nbTilesY; ++row) + { + for(unsigned int col = 0; col < nbTilesX; ++col) + { + /* Local variables for the next loop */ + // Compute current tile start and size + const unsigned int startX = col * tileWidth; + const unsigned int startY = row * tileHeight; // Upper left coordinates of the tile. + unsigned int sizeX = tileWidth; + unsigned int sizeY = tileHeight; // Size of the tiles. + + // Current tile size might be different for right and bottom borders + if (col == nbTilesX -1) + { + sizeX += imageWidth % tileWidth; + } + if (row == nbTilesY -1) + { + sizeY += imageHeight % tileHeight; + } + + /* Margin at the top ? */ + if( row > 0 ) + { + tiles[i].margin[POS_TOP] = margin; + tiles[i].rows[0] = row * tileHeight; + tiles[i].tileNeighbors[NBH_TOP] = i - nbTilesX; + } + else + { + // Tile is on the top row --> no top margin + tiles[i].margin[POS_TOP] = 0; + tiles[i].rows[0] = 0; + tiles[i].tileNeighbors[NBH_TOP] = -1; + } + + /* Margin at the right */ + if( col < nbTilesX - 1 ) + { + tiles[i].margin[POS_RIGHT] = margin; + tiles[i].columns[1] = col * tileWidth + sizeX - 1; //sizeX + tiles[i].tileNeighbors[NBH_RIGHT] = i+1; + } + else + { + // Tile is on the right column --> no right margin + tiles[i].margin[POS_RIGHT] = 0; + tiles[i].columns[1] = imageWidth - 1; + tiles[i].tileNeighbors[NBH_RIGHT] = -1; + } + + /* Margin at the bottom */ + if( row < nbTilesY - 1) + { + tiles[i].margin[POS_BOTTOM] = margin; + tiles[i].rows[1] = row * tileHeight + sizeY - 1; // sizeY + tiles[i].tileNeighbors[NBH_BOTTOM] = i + nbTilesX; + } + else + { + // Tile is on the bottom --> no bottom margin + tiles[i].margin[POS_BOTTOM] = 0; + tiles[i].rows[1] = imageHeight - 1; + tiles[i].tileNeighbors[NBH_BOTTOM] = -1; + } + + /* Margin at the left */ + if( col > 0 ) + { + tiles[i].margin[POS_LEFT] = margin; + tiles[i].columns[0] = col * tileWidth; + tiles[i].tileNeighbors[NBH_LEFT] = i-1; + } + else + { + // Tile is on the left --> no left margin + tiles[i].margin[POS_LEFT] = 0; + tiles[i].columns[0] = 0; + tiles[i].tileNeighbors[NBH_LEFT] = -1; + } + + /* Store the tile region */ + typename TInputImage::IndexType index; + index[0] = startX - tiles[i].margin[POS_LEFT]; + index[1] = startY - tiles[i].margin[POS_TOP]; + typename TInputImage::SizeType size; + size[0] = sizeX + tiles[i].margin[POS_LEFT] + tiles[i].margin[POS_RIGHT]; + size[1] = sizeY + tiles[i].margin[POS_TOP] + tiles[i].margin[POS_BOTTOM]; + typename TInputImage::RegionType region(index, size); + tiles[i].region = region; + + // std::cout << "Tile " << i << ": start at " << index << " with size " << size << std::endl; + + /* Is there a neighbor at the rop right */ + if(row > 0 && col < nbTilesX - 1) + tiles[i].tileNeighbors[NBH_TOP_RIGHT] = i - nbTilesX + 1; + else + tiles[i].tileNeighbors[NBH_TOP_RIGHT] = -1; + + /* Is there a neighbor at the bottom right */ + if(col < nbTilesX - 1 && row < nbTilesY - 1) + tiles[i].tileNeighbors[NBH_BOTTOM_RIGHT] = i + nbTilesX + 1; + else + tiles[i].tileNeighbors[NBH_BOTTOM_RIGHT] = -1; + + /* Is there a neighbor at the bottom left */ + if(row < nbTilesY - 1 && col > 0) + tiles[i].tileNeighbors[NBH_BOTTOM_LEFT] = i + nbTilesX - 1; + else + tiles[i].tileNeighbors[NBH_BOTTOM_LEFT] = -1; + + /* Is there a neighbor at the top left */ + if(col > 0 && row > 0) + tiles[i].tileNeighbors[NBH_TOP_LEFT] = i - nbTilesX - 1; + else + tiles[i].tileNeighbors[NBH_TOP_LEFT] = -1; + + const std::string suffix = std::to_string(row) + "_" + std::to_string(col) + ".tif"; + tiles[i].nodeFileName = temporaryFilesPrefix + "_node_" + suffix; + tiles[i].edgeFileName = temporaryFilesPrefix + "_edge_" + suffix; + tiles[i].nodeMarginFileName = temporaryFilesPrefix + "_nodeMargin_" + suffix; + tiles[i].edgeMarginFileName = temporaryFilesPrefix + "_edgeMargin_" + suffix; + + i++; + } // end for(unsigned int col = 0; col < nbTilesX; ++col) + + } // for(unsigned int row = 0; row < nbTilesY; ++row) + + return tiles; + } +} //end namespace otb +#endif + diff --git a/include/otbSLICScheduler.txx~ b/include/otbSLICScheduler.txx~ new file mode 100644 index 0000000000000000000000000000000000000000..c01946d8d79ba51b264f5adc259a320f85ce048e --- /dev/null +++ b/include/otbSLICScheduler.txx~ @@ -0,0 +1,552 @@ +#ifndef otbSLICScheduler_txx +#define otbSLICScheduler_txx + +#include "otbSLICScheduler.h" + +namespace otb{ + + template <class TInputImage, class TOutputLabelImage> + SLICScheduler<TInputImage, TOutputLabelImage> + ::SLICScheduler() : + m_SpatialWidth(100), + m_SpatialDistanceWeight(1), + m_MaxIterationNumber(10), + m_Threshold(0.1), + m_NbTilesX(1), + m_NbTilesY(1), + m_Margin(0) + {} + + template <class TInputImage, class TOutputLabelImage> + SLICScheduler<TInputImage, TOutputLabelImage> + ::~SLICScheduler() + {} + + template <class TInputImage, class TOutputLabelImage> + void + SLICScheduler<TInputImage, TOutputLabelImage> + ::SetInputImage(typename TInputImage::Pointer InputImagePointer) + { + m_InputImage = InputImagePointer; + } + + template <class TInputImage, class TOutputLabelImage> + void + SLICScheduler<TInputImage, TOutputLabelImage> + ::Run() + { + typename otb::MPIConfig::Pointer mpiConfig = otb::MPIConfig::Instance(); + unsigned int myRank = mpiConfig->GetMyRank(); + unsigned int nbProcs = mpiConfig->GetNbProcs(); + + const unsigned int imageSizeX = m_InputImage->GetLargestPossibleRegion().GetSize()[0]; + const unsigned int imageSizeY = m_InputImage->GetLargestPossibleRegion().GetSize()[1]; + + const unsigned int tileWidth = imageSizeX/m_NbTilesX; + const unsigned int tileHeight = imageSizeY/m_NbTilesY; + const unsigned int pixelMargin = m_Margin*m_SpatialWidth; + + typedef std::vector<otb::ProcessingTile> TileListType; + + TileListType tilesNoMargin = SplitOTBImage(m_InputImage, tileWidth, tileHeight, 0, m_NbTilesX, m_NbTilesY, m_Prefix); + + TileListType tilesMargin = SplitOTBImage(m_InputImage, tileWidth, tileHeight, pixelMargin, m_NbTilesX, m_NbTilesY, m_Prefix); + + TileListType whiteTiles; + TileListType blackTiles; + TileListType tilesToWrite; + //Margin mode + if (m_Margin > 0) { + for(unsigned int y = 0 ; y < m_NbTilesY; ++y){ + for(unsigned int x = 0 ; x < m_NbTilesX; ++x){ + if(!((x+y)%2)){ //if x + y even + blackTiles.push_back(tilesNoMargin[x+m_NbTilesX*y]); + } + else{ + whiteTiles.push_back(tilesMargin[x+m_NbTilesX*y]); + } + } + } + if(myRank < blackTiles.size()) + { + unsigned int splitIdx = myRank; + while(splitIdx < blackTiles.size()) + { + tilesToWrite.push_back(blackTiles[splitIdx]); + splitIdx+=nbProcs; + } + } + } + //Simple streaming mode + else{ + if(myRank < tilesNoMargin.size()) + { + unsigned int splitIdx = myRank; + while(splitIdx < tilesNoMargin.size()) + { + tilesToWrite.push_back(tilesNoMargin[splitIdx]); + splitIdx+=nbProcs; + } + } + } + typedef otb::SLICFilter<TInputImage, TOutputLabelImage> SLICFilter; + typedef itk::ExtractImageFilter<TInputImage,TInputImage> ExtractFilterType; + typedef otb::ImageFileReader<TInputImage> ReaderType; + + //Black squares + for (otb::ProcessingTile const& t : tilesToWrite) + { + typename ReaderType::Pointer reader = ReaderType::New(); + reader->SetFileName(m_InputName); + reader->SetReleaseDataFlag(true); + + typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); + extractFilter->SetReleaseDataFlag(true); + extractFilter->SetInput(reader->GetOutput()); + extractFilter->SetExtractionRegion(t.region); + + typename SLICFilter::Pointer filter = SLICFilter::New(); + filter->SetReleaseDataFlag(true); + filter->SetSpatialWidth(m_SpatialWidth); + filter->SetSpatialDistanceWeight(m_SpatialDistanceWeight); + filter->SetMaxIterationNumber(m_MaxIterationNumber); + filter->SetThreshold(m_Threshold); + filter->SetNbSPx(vcl_ceil((float) m_InputImage->GetLargestPossibleRegion().GetSize()[0]/m_SpatialWidth)); + filter->SetTile(t); + filter->SetMargin(m_Margin); + filter->SetInput(extractFilter->GetOutput()); + + typename WriterType::Pointer writer = WriterType::New(); + std::stringstream name; + name << m_Prefix << "_" << t.region.GetIndex()[0] << "_" << t.region.GetIndex()[1] << "_" << t.region.GetSize()[0] << "_" << t.region.GetSize()[1] << ".tif"; + writer->SetInput(scheduler->GetOutput()); + writer->SetFileName(name.str()); + writer->SetNumberOfDivisionsStrippedStreaming(1); + writer->Update(); + std::cout << myRank << " writing b region :" << name.str() << std::endl; + } + + mpiConfig->barrier(); + + if(m_Margin > 0){ + tilesToWrite.clear(); + for(unsigned int line = 0 ; line < m_NbTilesY ; line++){ + unsigned int nbWhite = vcl_floor(m_NbTilesX/2); + if (m_NbTilesX%2){ + nbWhite += line%2; + } + if(myRank < nbWhite) + { + unsigned int splitIdx = myRank; + while(splitIdx < nbWhite) + { + unsigned int ind; + if(m_NbTilesX%2) + ind = m_NbTilesX*vcl_floor(line/2) + splitIdx + vcl_floor(m_NbTilesX/2)*(line%2); + else + ind = splitIdx + nbWhite*line; + tilesToWrite.push_back(whiteTiles[ind]); + splitIdx+=nbProcs; + } + } + typedef otb::ImageFileReader<TOutputLabelImage> LabelReaderType; + typedef itk::ExtractImageFilter<TOutputLabelImage, TOutputLabelImage> LabelExtractFilterType; + for (otb::ProcessingTile t : tilesToWrite) + { + typename ReaderType::Pointer reader = ReaderType::New(); + reader->SetFileName(m_InputName); + reader->SetReleaseDataFlag(true); + typename SLICScheduler::Pointer scheduler = SLICScheduler::New(); + scheduler->SetSpatialWidth(m_SpatialWidth); + scheduler->SetSpatialDistanceWeight(m_SpatialDistanceWeight); + scheduler->SetMaxIterationNumber(m_MaxIterationNumber); + scheduler->SetThreshold(m_Threshold); + scheduler->SetNbSPx(vcl_ceil((float) m_InputImage->GetLargestPossibleRegion().GetSize()[0]/m_SpatialWidth)); + scheduler->SetMargin(m_Margin); + typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); + extractFilter->SetReleaseDataFlag(true); + extractFilter->SetInput(reader->GetOutput()); + //Extract main region + extractFilter->SetExtractionRegion(t.region); + scheduler->SetTile(t); + scheduler->ClearMargins(); + scheduler->SetInput(extractFilter->GetOutput()); + scheduler->SetReleaseDataFlag(true); + //Read neighboring black tiles margin areas and load into filter + for (unsigned int i = 0; i < 8 ; i++){ + if(pixelMargin > 0 && t.tileNeighbors[i]>=0){ + typename TOutputLabelImage::RegionType marginRegion; + typename TOutputLabelImage::RegionType r; + bool read = false; + switch (i){ + case otb::NBH_TOP:{ + //Normal case + r = tilesNoMargin[t.tileNeighbors[i]].region; + // typename TOutputLabelImage::RegionType::IndexType index = r.GetIndex(); + typename TOutputLabelImage::RegionType::IndexType index({0,0}); + typename TOutputLabelImage::RegionType::SizeType size({r.GetSize()[0],pixelMargin}); + index[1]+=r.GetSize()[1]-pixelMargin; + //Accomodate for corners + if(t.tileNeighbors[otb::NBH_LEFT]>=0){ + //if left neighbor + index[0]+=pixelMargin; + size[0]-=pixelMargin; + + } + if(t.tileNeighbors[otb::NBH_RIGHT]>=0){ + //if right neighbor + size[0]-=pixelMargin; + } + marginRegion.SetIndex(index); + marginRegion.SetSize(size); + read = true; + break; + } + case otb::NBH_BOTTOM:{ + r = tilesNoMargin[t.tileNeighbors[i]].region; + // typename TOutputLabelImage::RegionType::IndexType index = r.GetIndex(); + typename TOutputLabelImage::RegionType::IndexType index({0,0}); + const typename TOutputLabelImage::RegionType::SizeType size({r.GetSize()[0],pixelMargin}); + marginRegion.SetIndex(index); + marginRegion.SetSize(size); + read = true; + break; + } + case otb::NBH_LEFT:{ + r = tilesNoMargin[t.tileNeighbors[i]].region; + // typename TOutputLabelImage::RegionType::IndexType index = r.GetIndex(); + typename TOutputLabelImage::RegionType::IndexType index({0,0}); + index[0]+=r.GetSize()[0]-pixelMargin; + typename TOutputLabelImage::RegionType::SizeType size({pixelMargin,r.GetSize()[1]}); + if(t.tileNeighbors[otb::NBH_TOP]>=0){ + //if right neighbor + index[1]+=pixelMargin; + size[1]-=pixelMargin; + } + marginRegion.SetIndex(index); + marginRegion.SetSize(size); + read = true; + break; + } + case otb::NBH_RIGHT:{ + r = tilesNoMargin[t.tileNeighbors[i]].region; + // typename TOutputLabelImage::RegionType::IndexType index = r.GetIndex(); + typename TOutputLabelImage::RegionType::IndexType index({0,0}); + typename TOutputLabelImage::RegionType::SizeType size({pixelMargin,r.GetSize()[1]}); + if(t.tileNeighbors[otb::NBH_TOP]>=0){ + //if right neighbor + index[1]+=pixelMargin; + size[1]-=pixelMargin; + } + marginRegion.SetIndex(index); + marginRegion.SetSize(size); + read = true; + break; + } + case otb::NBH_TOP_LEFT: { + r = tilesMargin[t.tileNeighbors[i]].region; + // typename TOutputLabelImage::RegionType::IndexType index = r.GetIndex(); + typename TOutputLabelImage::RegionType::IndexType index({0,0}); + index[0]+=r.GetSize()[0] - 2*pixelMargin; + index[1]+=r.GetSize()[1] - 2*pixelMargin; + const typename TOutputLabelImage::RegionType::SizeType size({2*pixelMargin,2*pixelMargin}); + marginRegion.SetIndex(index); + marginRegion.SetSize(size); + read = true; + break; + } + case otb::NBH_TOP_RIGHT: { + r = tilesMargin[t.tileNeighbors[i]].region; + // typename TOutputLabelImage::RegionType::IndexType index = r.GetIndex(); + typename TOutputLabelImage::RegionType::IndexType index({0,0}); + index[1]+=r.GetSize()[1] - 2*pixelMargin; + const typename TOutputLabelImage::RegionType::SizeType size({2*pixelMargin,2*pixelMargin}); + marginRegion.SetIndex(index); + marginRegion.SetSize(size); + read = true; + break; + } + } + if(read){ + typename LabelExtractFilterType::Pointer marginExtract = LabelExtractFilterType::New(); + std::stringstream readNeighborName; + readNeighborName << m_Prefix <<"_"<< r.GetIndex()[0] << "_" << r.GetIndex()[1] << "_"<< r.GetSize()[0] << "_"<< r.GetSize()[1] << ".tif"; + typename LabelReaderType::Pointer labelReader = LabelReaderType::New(); + labelReader->SetReleaseDataFlag(true); + labelReader->SetFileName(readNeighborName.str()); + //Set the index of the output (reader gives 0 0) + marginExtract->SetInput(labelReader->GetOutput()); + marginExtract->SetExtractionRegion(marginRegion); + marginExtract->Update(); + marginRegion.SetIndex({marginRegion.GetIndex()[0]+r.GetIndex()[0],marginRegion.GetIndex()[1]+r.GetIndex()[1]}); + marginExtract->GetOutput()->SetRegions(marginRegion); + scheduler->AddInputMargin(marginExtract->GetOutput()); + } + } + } + scheduler->Update(); + typename WriterType::Pointer writer = WriterType::New(); + writer->SetInput(scheduler->GetOutput()); + std::stringstream name; + name << m_Prefix << "_" << t.region.GetIndex()[0] << "_" << t.region.GetIndex()[1] << "_" << t.region.GetSize()[0] << "_" << t.region.GetSize()[1] << ".tif"; + writer->SetFileName(name.str()); + writer->SetNumberOfDivisionsStrippedStreaming(1); + writer->Update(); + std::cout << myRank << " writing w region :" << name.str() << std::endl; + } + tilesToWrite.clear(); + mpiConfig->barrier(); + } + } + try + { + if (myRank == 0) + { + // VRT Filename + std::stringstream vrtfOut; + vrtfOut<< m_Prefix << ".vrt"; + + // Data type + GDALDataType dataType; + dataType = GDALGetDataTypeByName("UInt32"); + + // Get VRT driver + GDALAllRegister(); + GDALDriver *driver = GetGDALDriverManager()->GetDriverByName("VRT"); + if (driver == NULL) { + mpiConfig->logError("Error opening VRT driver."); + mpiConfig->abort(EXIT_FAILURE); + } + + // Create output raster + GDALDataset *VRTOutput = driver->Create(vrtfOut.str().c_str(), + imageSizeX, + imageSizeY, + 0, + dataType, + NULL); // No options + if (VRTOutput == NULL) { + mpiConfig->logError("driver->Create call failed.\n"); + mpiConfig->abort(EXIT_FAILURE); + } + + // Set GeoTransform + double gt[6]; + gt[0] = m_InputImage->GetOrigin()[0] - 0.5*m_InputImage->GetSignedSpacing()[0]; + gt[1] = m_InputImage->GetSignedSpacing()[0]; + gt[2] = 0.0; + gt[3] = m_InputImage->GetOrigin()[1] - 0.5*m_InputImage->GetSignedSpacing()[1]; + gt[4] = 0.0; + gt[5] = m_InputImage->GetSignedSpacing()[1]; + VRTOutput->SetGeoTransform(gt); + + // Set projection + OGRSpatialReference out_sr; + char *wkt = NULL; + out_sr.SetFromUserInput(m_InputImage->GetProjectionRef().c_str()); + out_sr.exportToWkt(&wkt); + VRTOutput->SetProjection(wkt); + VRTOutput->AddBand(dataType, NULL); + if(m_Margin == 0){ + writeVRTRegions(tilesNoMargin, VRTOutput, m_Prefix, 1); + } + else{ + writeVRTRegions(blackTiles, VRTOutput, m_Prefix, 1); + writeVRTRegions(whiteTiles, VRTOutput, m_Prefix, 1); + } + // Close + OGRFree(wkt); + GDALClose(VRTOutput); + } + } + catch (itk::ExceptionObject& err) + { + std::stringstream message; + message << "ExceptionObject caught: " << err << std::endl; + mpiConfig->logError(message.str()); + mpiConfig->abort(EXIT_FAILURE); + } + } + template<class TInputImage, class TOutputLabelImage> + void + SLICScheduler<TInputImage,TOutputLabelImage> + ::writeVRTRegions(TileListType const& inputRegions, GDALDataset *VRTOutput, std::string prefix, unsigned int band){ + for(otb::ProcessingTile t : inputRegions) + { + int tileSizeX = t.region.GetSize()[0]; + int tileSizeY = t.region.GetSize()[1]; + int tileIndexX = t.region.GetIndex()[0]; + int tileIndexY = t.region.GetIndex()[1]; + std::stringstream tileFileName; + tileFileName <<prefix<<"_"<<tileIndexX<<"_"<<tileIndexY<<"_"<<tileSizeX<<"_"<<tileSizeY<<".tif"; + // std::cout<<tileFileName.str()<<std::endl; + + GDALDataset *dataset = (GDALDataset*) GDALOpen(tileFileName.str().c_str(), GA_ReadOnly); + + VRTSourcedRasterBand *VRTBand = dynamic_cast<VRTSourcedRasterBand*> (VRTOutput->GetRasterBand(band)); + VRTBand->AddComplexSource(dataset->GetRasterBand(band), + 0,//Xoffset + 0,//Yoffset + tileSizeX,//Dimensions + tileSizeY,//Dimensions + tileIndexX,//Index + tileIndexY,//Index + tileSizeX,//Dimensions + tileSizeY,//Dimensions + 0.0,//Scale offset + 1,//Scale Ratio + 0);//NodataValue + } + } + template<class TInputImage, class TOutputLabelImage> + std::vector<ProcessingTile> + SLICScheduler<TInputImage,TOutputLabelImage> + ::SplitOTBImage(const TInputImage * imagePtr, // input image + const unsigned int tileWidth, // width of the tile + const unsigned int tileHeight, // height of the tile + const unsigned int margin, // stability margin + const unsigned int nbTilesX, + const unsigned int nbTilesY, + const std::string temporaryFilesPrefix) + { + std::vector<ProcessingTile> tiles; + // Image size + const unsigned int imageWidth = imagePtr->GetLargestPossibleRegion().GetSize()[0]; + const unsigned int imageHeight = imagePtr->GetLargestPossibleRegion().GetSize()[1]; + + /* Loop over the tiles*/ + tiles.assign(nbTilesX * nbTilesY, ProcessingTile()); + unsigned int i = 0; + for(unsigned int row = 0; row < nbTilesY; ++row) + { + for(unsigned int col = 0; col < nbTilesX; ++col) + { + /* Local variables for the next loop */ + // Compute current tile start and size + const unsigned int startX = col * tileWidth; + const unsigned int startY = row * tileHeight; // Upper left coordinates of the tile. + unsigned int sizeX = tileWidth; + unsigned int sizeY = tileHeight; // Size of the tiles. + + // Current tile size might be different for right and bottom borders + if (col == nbTilesX -1) + { + sizeX += imageWidth % tileWidth; + } + if (row == nbTilesY -1) + { + sizeY += imageHeight % tileHeight; + } + + /* Margin at the top ? */ + if( row > 0 ) + { + tiles[i].margin[POS_TOP] = margin; + tiles[i].rows[0] = row * tileHeight; + tiles[i].tileNeighbors[NBH_TOP] = i - nbTilesX; + } + else + { + // Tile is on the top row --> no top margin + tiles[i].margin[POS_TOP] = 0; + tiles[i].rows[0] = 0; + tiles[i].tileNeighbors[NBH_TOP] = -1; + } + + /* Margin at the right */ + if( col < nbTilesX - 1 ) + { + tiles[i].margin[POS_RIGHT] = margin; + tiles[i].columns[1] = col * tileWidth + sizeX - 1; //sizeX + tiles[i].tileNeighbors[NBH_RIGHT] = i+1; + } + else + { + // Tile is on the right column --> no right margin + tiles[i].margin[POS_RIGHT] = 0; + tiles[i].columns[1] = imageWidth - 1; + tiles[i].tileNeighbors[NBH_RIGHT] = -1; + } + + /* Margin at the bottom */ + if( row < nbTilesY - 1) + { + tiles[i].margin[POS_BOTTOM] = margin; + tiles[i].rows[1] = row * tileHeight + sizeY - 1; // sizeY + tiles[i].tileNeighbors[NBH_BOTTOM] = i + nbTilesX; + } + else + { + // Tile is on the bottom --> no bottom margin + tiles[i].margin[POS_BOTTOM] = 0; + tiles[i].rows[1] = imageHeight - 1; + tiles[i].tileNeighbors[NBH_BOTTOM] = -1; + } + + /* Margin at the left */ + if( col > 0 ) + { + tiles[i].margin[POS_LEFT] = margin; + tiles[i].columns[0] = col * tileWidth; + tiles[i].tileNeighbors[NBH_LEFT] = i-1; + } + else + { + // Tile is on the left --> no left margin + tiles[i].margin[POS_LEFT] = 0; + tiles[i].columns[0] = 0; + tiles[i].tileNeighbors[NBH_LEFT] = -1; + } + + /* Store the tile region */ + typename TInputImage::IndexType index; + index[0] = startX - tiles[i].margin[POS_LEFT]; + index[1] = startY - tiles[i].margin[POS_TOP]; + typename TInputImage::SizeType size; + size[0] = sizeX + tiles[i].margin[POS_LEFT] + tiles[i].margin[POS_RIGHT]; + size[1] = sizeY + tiles[i].margin[POS_TOP] + tiles[i].margin[POS_BOTTOM]; + typename TInputImage::RegionType region(index, size); + tiles[i].region = region; + + // std::cout << "Tile " << i << ": start at " << index << " with size " << size << std::endl; + + /* Is there a neighbor at the rop right */ + if(row > 0 && col < nbTilesX - 1) + tiles[i].tileNeighbors[NBH_TOP_RIGHT] = i - nbTilesX + 1; + else + tiles[i].tileNeighbors[NBH_TOP_RIGHT] = -1; + + /* Is there a neighbor at the bottom right */ + if(col < nbTilesX - 1 && row < nbTilesY - 1) + tiles[i].tileNeighbors[NBH_BOTTOM_RIGHT] = i + nbTilesX + 1; + else + tiles[i].tileNeighbors[NBH_BOTTOM_RIGHT] = -1; + + /* Is there a neighbor at the bottom left */ + if(row < nbTilesY - 1 && col > 0) + tiles[i].tileNeighbors[NBH_BOTTOM_LEFT] = i + nbTilesX - 1; + else + tiles[i].tileNeighbors[NBH_BOTTOM_LEFT] = -1; + + /* Is there a neighbor at the top left */ + if(col > 0 && row > 0) + tiles[i].tileNeighbors[NBH_TOP_LEFT] = i - nbTilesX - 1; + else + tiles[i].tileNeighbors[NBH_TOP_LEFT] = -1; + + const std::string suffix = std::to_string(row) + "_" + std::to_string(col) + ".tif"; + tiles[i].nodeFileName = temporaryFilesPrefix + "_node_" + suffix; + tiles[i].edgeFileName = temporaryFilesPrefix + "_edge_" + suffix; + tiles[i].nodeMarginFileName = temporaryFilesPrefix + "_nodeMargin_" + suffix; + tiles[i].edgeMarginFileName = temporaryFilesPrefix + "_edgeMargin_" + suffix; + + i++; + } // end for(unsigned int col = 0; col < nbTilesX; ++col) + + } // for(unsigned int row = 0; row < nbTilesY; ++row) + + return tiles; + } +} //end namespace otb +#endif + diff --git a/include/simplePointCalculator.h b/include/simplePointCalculator.h new file mode 100644 index 0000000000000000000000000000000000000000..0e7b278a0860b4500693d1ea44356cb938424ec9 --- /dev/null +++ b/include/simplePointCalculator.h @@ -0,0 +1,18 @@ +#ifndef simplePointCalculator_h +#define simplePointCalculator_h + +#include <array> +#include <vector> +#include <iostream> +#include <bitset> +template <class IteratorType, class LabelType> +class simplePointCalculator{ +public: + int isSimpleLabel (IteratorType const& neighborhoodLabelsBegin, IteratorType const& neighborhoodLabelsEnd, LabelType const& evalLabel) const; + void generateInitializerList(); +private: + constexpr static std::array<bool,256> T4Table{0,0,1,1,0,0,1,1,1,1,0,1,0,0,0,1,1,0,0,0,1,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,1,0,0,0,1,1,0,0,0,1,0,1,1,1,1,0,1,1,1,1,1}; +}; +#include "simplePointCalculator.txx" +#endif + diff --git a/include/simplePointCalculator.txx b/include/simplePointCalculator.txx new file mode 100644 index 0000000000000000000000000000000000000000..c5d2021612b4e33f9cea474291b92ca4d416490d --- /dev/null +++ b/include/simplePointCalculator.txx @@ -0,0 +1,88 @@ +#ifndef simplePointCalculator_txx +#define simplePointCalculator_txx + +#include "simplePointCalculator.h" + +template <class IteratorType, class LabelType> +constexpr +std::array<bool,256> +simplePointCalculator<IteratorType,LabelType>::T4Table; + +template <class IteratorType, class LabelType> +int +simplePointCalculator<IteratorType,LabelType> +::isSimpleLabel (IteratorType const& neighborhoodLabelsBegin, IteratorType const& neighborhoodLabelsEnd, LabelType const& evalLabel) const { + // read in table if evalLabel is simple wrt the neighborhoodLabels + // simpleKey is 8 bits representing the neighborhood in order + // b7 b6 b5 + // b4 XX b3 + // b2 b1 b0 + + unsigned char simpleKey = 0; + //read neighborhood backwards + IteratorType n = neighborhoodLabelsEnd-1; + for (; n > neighborhoodLabelsBegin-1; --n){ + //Discard central label + if(n != neighborhoodLabelsBegin+4) + { + if (*n == evalLabel) + { + simpleKey++; + } + if(n != neighborhoodLabelsBegin) + { + simpleKey <<= 1; + } + } + } + return simplePointCalculator<IteratorType,LabelType>::T4Table[simpleKey]; +} + +template <class IteratorType, class LabelType> +void +simplePointCalculator<IteratorType,LabelType> +::generateInitializerList(){ + std::array<bool,8> ib; + std::array<int,256> ar; + for(int i = 0 ; i < 255 ; ++i) + { + //Convert number to bit table + for (int bit = 0; bit < 8 ; ++bit) + { + ib[bit] = (i >> bit) & 1; + } + int n=0; + for (int bit = 1; bit < 8 ; ++bit) + { + n += (ib[bit] == 0 && ib[bit-1] == 1); + } + if(ib[7] == 1 && ib[0] == 0) + { + n++; + } + ar[i] = (n == 1)&&(ib[1] || ib[3] || ib[5] || ib[7]); + } + ar[255]=1; + bool correct = true; + std::cout<<"{"; + for (unsigned int i = 0 ; i < 256; i++) + { + std::bitset<8> newbits(i); + std::bitset<8> oldbits; + oldbits.set(0,newbits[0]); + oldbits.set(1,newbits[1]); + oldbits.set(2,newbits[2]); + oldbits.set(3,newbits[4]); + oldbits.set(4,newbits[7]); + oldbits.set(5,newbits[6]); + oldbits.set(6,newbits[5]); + oldbits.set(7,newbits[3]); + std::cout << ar[oldbits.to_ulong()] << (i < 255 ? "," : ""); + correct &= (ar[oldbits.to_ulong()] == simplePointCalculator<IteratorType,LabelType>::T4Table[i]); + } + std::cout << "}" << std::endl; + + std::cout << (correct ? "success" : "fail") << std::endl; +} +#endif + diff --git a/otb-module.cmake b/otb-module.cmake new file mode 100644 index 0000000000000000000000000000000000000000..359443e38383d925aef87a2adae5430474c5e9f4 --- /dev/null +++ b/otb-module.cmake @@ -0,0 +1,11 @@ +set(DOCUMENTATION "Large Scale OTB Application for image superpixel segmentation using SLIC algorithm.") + +otb_module(OTBSLIC + DEPENDS + OTBCommon + OTBApplicationEngine + OTBMPI + OTBGdalAdapters + OTBGDAL + + DESCRIPTION "Superpixel segmentation - SLIC algorithm") diff --git a/otb-module.cmake~ b/otb-module.cmake~ new file mode 100644 index 0000000000000000000000000000000000000000..fd25fd5dd313089836b15df359b2fa1a561442f2 --- /dev/null +++ b/otb-module.cmake~ @@ -0,0 +1,6 @@ +otb_module(OTBSLIC + DEPENDS + OTBCommon + OTBApplicationEngine + + DESCRIPTION "Superpixel segmentation - SLIC algorithm")