diff --git a/app/otbLSGRM.cxx b/app/otbLSGRM.cxx index b5dd444238db0afd5ef90225fc164f9688a86894..20e93301c98be2288df2a8468a477a00808fac88 100644 --- a/app/otbLSGRM.cxx +++ b/app/otbLSGRM.cxx @@ -17,6 +17,10 @@ #include "lsgrmFullLambdaScheduleSegmenter.h" #include "lsgrmController.h" +// Graph to label image (streaming version) +#include "otbStreamingGraphToImageFilter.h" +#include "otbStreamingImageVirtualWriter.h" + // system tools #include <itksys/SystemTools.hxx> @@ -65,6 +69,7 @@ private: void DoInit() { + SetName("GenericRegionMerging"); SetDescription("This application allows to use the Large Scale Generic Region Merging library " "(LSGRM) and provides currently 3 homogeneity criteria: Euclidean Distance, " @@ -107,6 +112,7 @@ private: AddChoice("tiling.none", "No tiling layout"); AddParameter(ParameterType_Int, "memory", "Restrict memory use (mb)"); + MandatoryOff("memory"); } @@ -128,28 +134,28 @@ private: std::string tmpdir; if (HasValue("tmpdir")) { - tmpdir= GetParameterAsString("tmpdir"); + tmpdir= GetParameterAsString("tmpdir"); } else { - tmpdir = itksys::SystemTools::GetFilenamePath(outfname); + tmpdir = itksys::SystemTools::GetFilenamePath(outfname); } if (!tmpdir.empty()) { - // A temporary directory is specified: we check that it ends with a POSIX separator - if (tmpdir[tmpdir.size()-1] != '/') - { - // If not, we add the separator - tmpdir.append("/"); - } - - // Check that the directory exists - if (!itksys::SystemTools::FileExists(tmpdir.c_str(),false)) - { - otbAppLogFATAL("The directory " << tmpdir << " does not exist."); - } - otbAppLogINFO("Using temporary directory " << tmpdir); + // A temporary directory is specified: we check that it ends with a POSIX separator + if (tmpdir[tmpdir.size()-1] != '/') + { + // If not, we add the separator + tmpdir.append("/"); + } + + // Check that the directory exists + if (!itksys::SystemTools::FileExists(tmpdir.c_str(),false)) + { + otbAppLogFATAL("The directory " << tmpdir << " does not exist."); + } + otbAppLogINFO("Using temporary directory " << tmpdir); } @@ -162,17 +168,20 @@ private: * This function sets the generic parameters of a controller and runs the segmentation */ template<class TSegmenter> - UInt32ImageType::Pointer SetGenericParametersAndRunSegmentation(const typename TSegmenter::ParamType params){ + void + SetGenericParametersAndRunSegmentation(const typename TSegmenter::ParamType params){ // Instantiate the controller typedef typename lsgrm::Controller<TSegmenter> ControlerType; typename ControlerType::Pointer controller = ControlerType::New(); + using GraphType = typename ControlerType::GraphType; // Set specific parameters controller->SetSpecificParameters(params); // Set input image - controller->SetInputImage(GetParameterFloatVectorImage("in")); + ImageType::Pointer inputImage = GetParameterFloatVectorImage("in"); + controller->SetInputImage(inputImage); // Set threshold float thres = GetParameterFloat("threshold"); @@ -188,86 +197,92 @@ private: int inputTilingMode = GetParameterInt("tiling"); if (inputTilingMode == TILING_AUTO) { - // Automatic mode - controller->SetTilingModeAuto(); + // Automatic mode + controller->SetTilingModeAuto(); } else if (inputTilingMode == TILING_USER) { - // User mode - controller->SetTilingModeUser(); - controller->SetTileWidth(GetParameterInt("tiling.user.sizex")); - controller->SetTileHeight(GetParameterInt("tiling.user.sizey")); - controller->SetNumberOfFirstIterations(GetParameterInt("tiling.user.nfirstiter")); + // User mode + controller->SetTilingModeUser(); + controller->SetTileWidth(GetParameterInt("tiling.user.sizex")); + controller->SetTileHeight(GetParameterInt("tiling.user.sizey")); + controller->SetNumberOfFirstIterations(GetParameterInt("tiling.user.nfirstiter")); } else if (inputTilingMode == TILING_NONE) { - // None mode - controller->SetTilingModeNone(); + // None mode + controller->SetTilingModeNone(); } else { - otbAppLogFATAL("Unknown tiling mode!"); + otbAppLogFATAL("Unknown tiling mode!"); } // Input RAM value? if (HasValue("memory")) { - otbAppLogINFO("Setting maximum memory to " << GetParameterInt("memory") << " MBytes"); - controller->SetInternalMemoryAvailable(GetParameterInt("memory")); + otbAppLogINFO("Setting maximum memory to " << GetParameterInt("memory") << " MBytes"); + controller->SetInternalMemoryAvailable(GetParameterInt("memory")); } // Run the segmentation controller->RunSegmentation(); + // Prepare the label image source + typedef lsgrm::StreamingGraphToImageFilter<GraphType, UInt32ImageType> LabelImageSourceType; + typename LabelImageSourceType::Pointer labelImageSource = LabelImageSourceType::New(); + labelImageSource->SetGraph(controller->GetOutputGraph()); + labelImageSource->SetOutputSize(inputImage->GetLargestPossibleRegion().GetSize()); + labelImageSource->SetOutputOrigin(inputImage->GetOrigin()); + labelImageSource->SetOutputSpacing(inputImage->GetSpacing()); + labelImageSource->SetOutputProjectionRef(inputImage->GetProjectionRef()); + labelImageSource->GenerateOutputInformation(); + + m_LabelImageSource = static_cast<itk::ImageSource<UInt32ImageType>*>(labelImageSource); + + SetParameterOutputImage<UInt32ImageType>("out", m_LabelImageSource->GetOutput()); + + // TODO: find an intelligent value of RAM + if (dynamic_cast<OutputImageParameter*>(GetParameterByKey("out"))) + { + OutputImageParameter* paramDown = dynamic_cast<OutputImageParameter*>(GetParameterByKey("out")); + paramDown->SetRAMValue(256); + } + // Get temporary files list m_TemporaryFilesList = controller->GetTemporaryFilesList(); - // Return the label image - return controller->GetLabeledClusteredOutput(); } void DoExecute() { - /* - TODO: the output directory in case the global graph cannot fit in memory (lsgrmController.txx) - */ - // Input image ImageType::Pointer inputImage = GetParameterFloatVectorImage("in"); - // Output image - UInt32ImageType::Pointer labelImage = UInt32ImageType::New(); - // Switch criterion int inputCriterion = GetParameterInt("criterion"); if (inputCriterion == CRITERION_BAATZ) { - grm::BaatzParam params; - params.m_SpectralWeight = GetParameterFloat("criterion.bs.cw"); - params.m_ShapeWeight = GetParameterFloat("criterion.bs.sw"); - labelImage = SetGenericParametersAndRunSegmentation<BaatzSegmenterType>(params); + grm::BaatzParam params; + params.m_SpectralWeight = GetParameterFloat("criterion.bs.cw"); + params.m_ShapeWeight = GetParameterFloat("criterion.bs.sw"); + SetGenericParametersAndRunSegmentation<BaatzSegmenterType>(params); } else if (inputCriterion == CRITERION_SPRING) { - grm::SpringParam params; - labelImage = SetGenericParametersAndRunSegmentation<SpringSegmenterType>(params); + grm::SpringParam params; + SetGenericParametersAndRunSegmentation<SpringSegmenterType>(params); } else if (inputCriterion == CRITERION_FLS) { - grm::FLSParam params; - labelImage = SetGenericParametersAndRunSegmentation<FLSSegmenterType>(params); + grm::FLSParam params; + SetGenericParametersAndRunSegmentation<FLSSegmenterType>(params); } else { - otbAppLogFATAL("Unknow criterion!") + otbAppLogFATAL("Unknow criterion!") } - // Set output image projection, origin and spacing for labelImage - labelImage->SetProjectionRef(inputImage->GetProjectionRef()); - labelImage->SetOrigin(inputImage->GetOrigin()); - labelImage->SetSpacing(inputImage->GetSpacing()); - SetParameterOutputImage<UInt32ImageType>("out", labelImage); - } void AfterExecuteAndWriteOutputs() @@ -282,15 +297,16 @@ private: // Delete temporary files for (unsigned int i = 0 ; i < m_TemporaryFilesList.size() ; i++) { - if( remove(m_TemporaryFilesList.at(i).c_str() ) != 0 ) - { - otbAppLogWARNING( "Error deleting file " << m_TemporaryFilesList.at(i) ); - } + if( remove(m_TemporaryFilesList.at(i).c_str() ) != 0 ) + { + otbAppLogWARNING( "Error deleting file " << m_TemporaryFilesList.at(i) ); + } } } private: std::vector<std::string> m_TemporaryFilesList; + itk::ImageSource<UInt32ImageType>::Pointer m_LabelImageSource; }; // app class } // end of namespace wrapper diff --git a/include/lsgrmController.h b/include/lsgrmController.h index 9550c3261064b90335e37c7484de2b948b900c39..e538cbe9a596e2fc20c6075db87077d1453b2da9 100644 --- a/include/lsgrmController.h +++ b/include/lsgrmController.h @@ -6,6 +6,7 @@ #include "itkObject.h" #include "itkMacro.h" + namespace lsgrm { template<class TSegmenter> @@ -30,6 +31,7 @@ public: using ImageType = typename SegmenterType::ImageType; using LabelImageType = typename SegmenterType::LabelImageType; using SegmentationParameterType = typename SegmenterType::ParamType; + using GraphType = typename SegmenterType::GraphType; /* Default constructor and destructor. */ Controller(); @@ -48,7 +50,6 @@ public: void SetTilingModeUser(){m_TilingMode = LSGRM_TILING_USER; Modified();}; void SetTilingModeAuto(){m_TilingMode = LSGRM_TILING_AUTO; Modified();}; - typename LabelImageType::Pointer GetLabeledClusteredOutput(); std::vector<std::string> GetTemporaryFilesList(); itkGetMacro(Margin, unsigned int); @@ -77,6 +78,8 @@ public: itkGetMacro(TileHeight, unsigned int); itkSetMacro(TileHeight, unsigned int); + itkGetMacro(OutputGraph, GraphType); + private: /** Enum for tiling mode */ @@ -91,7 +94,7 @@ private: void ComputeMaximumStabilityMargin(unsigned int width, unsigned int height, unsigned int &iter, unsigned int &margin); void CheckMemorySize(); unsigned int GetNodeMemory(); - long unsigned int GetMaximumNumberOfNodesInMemory(); + std::size_t GetMaximumNumberOfNodesInMemory(); /* Parameters given by the user */ std::string m_TemporaryFilesPrefix; // Path used to store intermediate files during the process. @@ -116,7 +119,8 @@ private: LSGRMTilingMode m_TilingMode; // tiling mode (none/user/auto) unsigned int m_Margin; // stability margin related to m_NumberOfFirstIterations std::vector<ProcessingTile> m_Tiles; // list of tiles - typename LabelImageType::Pointer m_LabelImage; // output label image + GraphType m_OutputGraph; // Output graph + }; } // end of namespace lsgrm diff --git a/include/lsgrmController.txx b/include/lsgrmController.txx index 2d2ee411556b605d29a765d013234d3ed41fdb23..6e072f0f9e7d803caa72777bf85ba09b42b10bdc 100644 --- a/include/lsgrmController.txx +++ b/include/lsgrmController.txx @@ -18,6 +18,7 @@ Controller<TSegmenter>::Controller() m_NbTilesY = 0; m_Threshold = 75; m_Memory = 0; + } template<class TSegmenter> @@ -87,6 +88,7 @@ void Controller<TSegmenter>::RunSegmentation() // Run first partial segmentation boost::timer t; t.restart(); + auto accumulatedMemory = RunFirstPartialSegmentation<TSegmenter>( m_InputImage, m_SpecificParameters, @@ -139,7 +141,7 @@ void Controller<TSegmenter>::RunSegmentation() numberOfIterationsRemaining -= numberOfIterationsForPartialSegmentations; } } - + #ifdef OTB_USE_MPI // Only the master process is doing the next part // TODO: Use the MPI process wich has the largest amount of memory @@ -150,7 +152,7 @@ void Controller<TSegmenter>::RunSegmentation() if(accumulatedMemory <= m_Memory) { // Merge all the graphs - m_LabelImage = MergeAllGraphsAndAchieveSegmentation<TSegmenter>( + m_OutputGraph = MergeAllGraphsAndAchieveSegmentation<TSegmenter>( m_SpecificParameters, m_Threshold, m_Tiles, @@ -195,14 +197,15 @@ void Controller<TSegmenter>::RunSegmentation() segmenter.SetInput(m_InputImage); segmenter.Update(); - // Get label image - m_LabelImage = segmenter.GetLabeledClusteredOutput(); + m_OutputGraph = segmenter.m_Graph; } else { itkExceptionMacro(<<"Unknow tiling mode!"); } + // TODO: [MPI] broadcast the graph to other nodes + } /* @@ -264,7 +267,7 @@ void Controller<TSegmenter>::CheckMemorySize() * Compute the maximum number of nodes which can fit in the memory */ template<class TSegmenter> -long unsigned int Controller<TSegmenter>::GetMaximumNumberOfNodesInMemory() +std::size_t Controller<TSegmenter>::GetMaximumNumberOfNodesInMemory() { itkDebugMacro(<< "Computing maximum number of nodes in memory"); @@ -433,16 +436,16 @@ void Controller<TSegmenter>::SetSpecificParameters(const SegmentationParameterTy m_SpecificParameters = params; } -template<class TSegmenter> -typename Controller<TSegmenter>::LabelImageType::Pointer -Controller<TSegmenter>::GetLabeledClusteredOutput() -{ -#ifdef OTB_USE_MPI - // Get the label image from the master process (the one which achieves segmentation) - BroadcastImage<typename TSegmenter::LabelImageType>(m_LabelImage); -#endif - return m_LabelImage; -} +//template<class TSegmenter> +//typename Controller<TSegmenter>::LabelImageType::Pointer +//Controller<TSegmenter>::GetLabeledClusteredOutput() +//{ +//#ifdef OTB_USE_MPI +// // Get the label image from the master process (the one which achieves segmentation) +// BroadcastImage<typename TSegmenter::LabelImageType>(m_LabelImage); +//#endif +// return m_LabelImage; +//} template <class TSegmenter> std::vector<std::string> Controller<TSegmenter>::GetTemporaryFilesList() diff --git a/include/lsgrmGraphOperations.h b/include/lsgrmGraphOperations.h index ea975fbe1126e6a65e0b782cdf63dda561a484d6..3ce66f2d87d3ce5589f312e41a1ceb68fa43926f 100644 --- a/include/lsgrmGraphOperations.h +++ b/include/lsgrmGraphOperations.h @@ -5,7 +5,6 @@ #include "grmGraphOperations.h" #include "otbVectorImage.h" #include "otbMultiChannelExtractROI.h" -#include "itkGrayscaleFillholeImageFilter.h" namespace lsgrm { @@ -32,7 +31,7 @@ typename TSegmenter::ImageType::Pointer ReadImageRegion( typename TSegmenter::ImageType::RegionType region); template<class TSegmenter> -typename TSegmenter::LabelImageType::Pointer +typename TSegmenter::GraphType MergeAllGraphsAndAchieveSegmentation( const typename TSegmenter::ParamType& params, const float& threshold, @@ -79,13 +78,13 @@ void RemoveUselessNodes(ProcessingTile& tile, template<class TSegmenter> -void UpdateNeighborsOfNoneDuplicatedNodes(std::unordered_map<long unsigned int, +void UpdateNeighborsOfNoneDuplicatedNodes(std::unordered_map<std::size_t, std::vector<typename TSegmenter::NodePointerType> >& borderPixelMap, const unsigned int imageWidth, const unsigned int imageHeight); template<class TSegmenter> -void RemoveDuplicatedNodes(std::unordered_map<long unsigned int, +void RemoveDuplicatedNodes(std::unordered_map<std::size_t, std::vector<typename TSegmenter::NodePointerType> >& borderPixelMap, typename TSegmenter::GraphType& graph, const unsigned int imageWidth); @@ -97,9 +96,10 @@ void BuildBorderPixelMap(typename TSegmenter::GraphType& graph, const unsigned int colTile, const unsigned int nbTilesX, const unsigned int nbTilesY, - std::unordered_map<long unsigned int, + std::unordered_map<std::size_t, std::vector<typename TSegmenter::NodePointerType> >& borderPixelMap, - const unsigned int imageWidth); + const unsigned int imageWidth, + bool merging = false); template<class TSegmenter> void InsertNodesFromTile(typename TSegmenter::GraphType& graph, diff --git a/include/lsgrmGraphOperations.txx b/include/lsgrmGraphOperations.txx index 930b8b32754bf16e5855bf43f11b296e347e16a7..d2094771191aa3a9c7811ea4cbb7979b59c2a7fc 100644 --- a/include/lsgrmGraphOperations.txx +++ b/include/lsgrmGraphOperations.txx @@ -25,7 +25,7 @@ typename TSegmenter::ImageType::Pointer ReadImageRegion( } template<class TSegmenter> -typename TSegmenter::LabelImageType::Pointer +typename TSegmenter::GraphType MergeAllGraphsAndAchieveSegmentation( const typename TSegmenter::ParamType& params, const float& threshold, @@ -63,13 +63,13 @@ MergeAllGraphsAndAchieveSegmentation( { std::cout << "Cleaning nodes of tile " << (row*nbTilesX + col) << " / " << (nbTilesX*nbTilesY) << std::endl; - std::unordered_map<long unsigned int, + std::unordered_map<std::size_t, std::vector<typename TSegmenter::NodePointerType> > borderPixelMap; std::cout << "\tBuildBorderPixelMap..." << std::endl; BuildBorderPixelMap<TSegmenter>(segmenter.m_Graph, tiles[row*nbTilesX + col], row, col, - nbTilesX, nbTilesY, borderPixelMap, imageWidth); + nbTilesX, nbTilesY, borderPixelMap, imageWidth, true); std::cout << "\tRemoveDuplicatedNodes..." << std::endl; @@ -91,69 +91,42 @@ MergeAllGraphsAndAchieveSegmentation( segmenter.SetThreshold(threshold); segmenter.SetDoFastSegmentation(false); // was true segmenter.SetNumberOfIterations(numberOfIterations); - grm::GraphOperations<TSegmenter>::PerfomAllIterationsWithLMBFAndConstThreshold(segmenter); - // // Write output graph to the output graph directory - // WriteGraph<TSegmenter>(segmenter.m_Graph, tmpDir, 0, 0); - - // Generate the label image - typename TSegmenter::LabelImageType::IndexType index; - index.Fill(0); - typename TSegmenter::LabelImageType::SizeType size; - size[0] = imageWidth; - size[1] = imageHeight; - typename TSegmenter::LabelImageType::RegionType region(index, size); - const typename TSegmenter::LabelImageType::InternalPixelType noDataLabel = 0; - typename TSegmenter::LabelImageType::Pointer labelImage = TSegmenter::LabelImageType::New(); - labelImage->SetRegions(region); - labelImage->Allocate(); - labelImage->FillBuffer(noDataLabel); - - using LabelImageIterator = itk::ImageRegionIterator<typename TSegmenter::LabelImageType>; - LabelImageIterator it(labelImage, labelImage->GetLargestPossibleRegion()); - - // Start at 1 (value 0 can be used for invalid pixels) - unsigned int label = 1; - for(auto& node : segmenter.m_Graph.m_Nodes) - { - lp::CellLists borderPixels; - lp::ContourOperations::GenerateBorderCells(borderPixels, node->m_Contour, node->m_Id, imageWidth); - - for (auto& pix: borderPixels) - { - index[0] = pix % imageWidth; - index[1] = pix / imageWidth; - labelImage->SetPixel(index, label); - } - ++label; - } - - // Re-order nodes labels (left->right to top->bottom) - vnl_vector<typename TSegmenter::LabelImageType::InternalPixelType> lut(label,noDataLabel); - label = 1; - for(it.GoToBegin();!it.IsAtEnd(); ++it) - { - unsigned int inputLabel = it.Get(); - if (lut[ inputLabel ] == noDataLabel && inputLabel != noDataLabel) - { - lut[ inputLabel ] = label; - label++; - } - } - - // Apply LUT - for(it.GoToBegin();!it.IsAtEnd(); ++it) - it.Set(lut[it.Get()]); - - // Fill holes - typedef itk::GrayscaleFillholeImageFilter<typename TSegmenter::LabelImageType, - typename TSegmenter::LabelImageType> FillholeFilterType; - typename FillholeFilterType::Pointer fillFilter = FillholeFilterType::New(); - fillFilter->SetInput(labelImage); - fillFilter->Update(); - - return fillFilter->GetOutput();} + // patch raf (sort using node id directly --> seems ok) + // Sort the nodes top-->down and left-->right + /* + std::sort(segmenter.m_Graph.m_Nodes.begin(), segmenter.m_Graph.m_Nodes.end(), + [imageWidth](const auto & a, const auto & b) -> bool + { + lp::CellLists borderPixelsA, borderPixelsB; + lp::ContourOperations::GenerateBorderCells(borderPixelsA, a->m_Contour, a->m_Id, imageWidth); + lp::ContourOperations::GenerateBorderCells(borderPixelsB, b->m_Contour, b->m_Id, imageWidth); + + // patch raf (were unsigned int) + std::size_t offA = 0; + std::size_t offB = 0; + // end patch + for (auto& pix: borderPixelsA) + if (pix>offA) + offA=pix; + + for (auto& pix: borderPixelsB) + if (pix>offB) + offB=pix; + + return offA > offB; + }); + */ + std::sort(segmenter.m_Graph.m_Nodes.begin(), segmenter.m_Graph.m_Nodes.end(), + [](const auto & a, const auto & b) -> bool + { + return a->m_Id < b->m_Id; + }); + // end patch + + return segmenter.m_Graph; +} template<class TSegmenter> long long unsigned int RunPartialSegmentation(const typename TSegmenter::ParamType& params, @@ -198,7 +171,7 @@ long long unsigned int RunPartialSegmentation(const typename TSegmenter::ParamTy row, col, nbTilesX, nbTilesY); std::cout << "\tBuild border pixel map..." << std::endl; - std::unordered_map<long unsigned int, std::vector<typename TSegmenter::NodePointerType> > borderPixelMap; + std::unordered_map<std::size_t, std::vector<typename TSegmenter::NodePointerType> > borderPixelMap; BuildBorderPixelMap<TSegmenter>(segmenter.m_Graph, currentTile, row, col, nbTilesX, nbTilesY, borderPixelMap, imageWidth); @@ -366,7 +339,7 @@ void RemoveUselessNodes(ProcessingTile& tile, } template<class TSegmenter> -void UpdateNeighborsOfNoneDuplicatedNodes(std::unordered_map<long unsigned int, +void UpdateNeighborsOfNoneDuplicatedNodes(std::unordered_map<std::size_t, std::vector<typename TSegmenter::NodePointerType> >& borderPixelMap, const unsigned int imageWidth, const unsigned int imageHeight) @@ -376,7 +349,7 @@ void UpdateNeighborsOfNoneDuplicatedNodes(std::unordered_map<long unsigned int, for(auto& pn : borderPixelMap) { - long int neighborhood[4]; + NeighIDType neighborhood[4]; grm::FOURNeighborhood(neighborhood, pn.first, imageWidth, imageHeight); for(short j = 0; j < 4; ++j) @@ -404,7 +377,7 @@ void UpdateNeighborsOfNoneDuplicatedNodes(std::unordered_map<long unsigned int, { if(borderPixelMap.find(pix) != borderPixelMap.end()) { - long int pixNeighborhood[4]; + NeighIDType pixNeighborhood[4]; grm::FOURNeighborhood(pixNeighborhood, pix, imageWidth, imageHeight); for(short k = 0; k < 4; k++) @@ -431,7 +404,7 @@ void UpdateNeighborsOfNoneDuplicatedNodes(std::unordered_map<long unsigned int, } template<class TSegmenter> -void RemoveDuplicatedNodes(std::unordered_map<long unsigned int, +void RemoveDuplicatedNodes(std::unordered_map<std::size_t, std::vector<typename TSegmenter::NodePointerType> >& borderPixelMap, typename TSegmenter::GraphType& graph, const unsigned int imageWidth) @@ -502,9 +475,10 @@ void BuildBorderPixelMap(typename TSegmenter::GraphType& graph, const unsigned int colTile, const unsigned int nbTilesX, const unsigned int nbTilesY, - std::unordered_map<long unsigned int, + std::unordered_map<std::size_t, std::vector<typename TSegmenter::NodePointerType> >& borderPixelMap, - const unsigned int imageWidth) + const unsigned int imageWidth, + bool merging) { unsigned int rowPixel, colPixel; unsigned int rowMin = (tile.rows[0] > 0) ? tile.rows[0] - 1 : tile.rows[0]; @@ -521,6 +495,15 @@ void BuildBorderPixelMap(typename TSegmenter::GraphType& graph, { continue; } + // patch raf (exclude nodes all outside tiles - on merging stage only) + else if(merging && (node->m_Bbox.m_UX > tile.columns[1] || + node->m_Bbox.m_UY > tile.rows[1] || + node->m_Bbox.m_UX + node->m_Bbox.m_W - 1 < tile.columns[0] || + node->m_Bbox.m_UY + node->m_Bbox.m_H - 1 < tile.rows[0])) + { + continue; + } + // end patch else { lp::CellLists borderPixels; @@ -683,7 +666,7 @@ long long unsigned int RunFirstPartialSegmentation( segmenter.SetNumberOfIterations(niter); segmenter.SetInput(imageTile); segmenter.Update(); - + if(segmenter.GetComplete() == false) isFusion = true; @@ -696,7 +679,7 @@ long long unsigned int RunFirstPartialSegmentation( tileWidth, tileHeight, imageWidth); - + // Remove unstable segments std::cout << "\tRemoving unstable segments..." << std::endl; RemoveUnstableSegments<TSegmenter>(segmenter.m_Graph, currentTile, imageWidth); @@ -1029,7 +1012,7 @@ void RescaleGraph(typename TSegmenter::GraphType& graph, // Start pixel index of the node (in the image) rowNodeImg = rowTile * tileHeight + rowNodeTile - tile.margin[0]; colNodeImg = colTile * tileWidth + colNodeTile - tile.margin[3]; - node->m_Id = rowNodeImg * imageWidth + colNodeImg; + node->m_Id = (std::size_t)rowNodeImg * (std::size_t)imageWidth + (std::size_t)colNodeImg; // Change also its bounding box node->m_Bbox.m_UX = colTile * tileWidth + node->m_Bbox.m_UX - tile.margin[3]; diff --git a/include/lsgrmSegmenter.h b/include/lsgrmSegmenter.h index 7eed1430eef0d46a877c17887f95528f55ec0931..995722d70f7c0aa8860153a5cf2a118d6a5baca5 100644 --- a/include/lsgrmSegmenter.h +++ b/include/lsgrmSegmenter.h @@ -106,7 +106,7 @@ public: void ReadGraph(FILE * nodeStream, FILE * edgeStream) { - std::unordered_map<long unsigned int, NodePointerType> nodeMap; + std::unordered_map<std::size_t, NodePointerType> nodeMap; // Read the size of the graph { @@ -151,7 +151,7 @@ public: for(auto& node: this->m_Graph.m_Nodes) { - long unsigned int nodeId; + std::size_t nodeId; fread(&(nodeId), sizeof(nodeId), 1, edgeStream); assert(nodeId == node->m_Id); @@ -160,7 +160,7 @@ public: node->m_Edges.reserve(edgeSize); for(unsigned int b = 0; b < edgeSize; b++) { - long unsigned int targetId; + std::size_t targetId; unsigned int boundary; fread(&(targetId), sizeof(targetId), 1, edgeStream); fread(&(boundary), sizeof(boundary), 1, edgeStream); diff --git a/include/otbStreamingGraphToImageFilter.h b/include/otbStreamingGraphToImageFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..1bd3bb4ee32d36ca6fe22bad082569eb8baa23b8 --- /dev/null +++ b/include/otbStreamingGraphToImageFilter.h @@ -0,0 +1,102 @@ +/* + * otbStreamingGraphToImageFilter.h + * + * Created on: 6 nov. 2017 + * Author: cresson + */ + +#ifndef MODULES_REMOTE_LSGRM_INCLUDE_OTBSTREAMINGGRAPHTOIMAGEFILTER_H_ +#define MODULES_REMOTE_LSGRM_INCLUDE_OTBSTREAMINGGRAPHTOIMAGEFILTER_H_ + +#include "itkImageSource.h" +#include "itkExceptionObject.h" +#include "itkImageRegion.h" +#include "itkGrayscaleFillholeImageFilter.h" + +#include "grmGraphOperations.h" + +// Boost R-Tree +#include <boost/geometry.hpp> +#include <boost/geometry/geometries/point.hpp> +#include <boost/geometry/geometries/box.hpp> + +#include <boost/geometry/index/rtree.hpp> + +// to store queries results +#include <vector> + +// just for output +#include <iostream> +#include <boost/foreach.hpp> + +namespace bg = boost::geometry; +namespace bgi = boost::geometry::index; + +namespace lsgrm +{ + +template <class TGraph, class TLabelImage> +class ITK_EXPORT StreamingGraphToImageFilter : public itk::ImageSource<TLabelImage> +{ +public: + /** Standard class typedefs. */ + typedef StreamingGraphToImageFilter Self; + typedef itk::ImageSource<TLabelImage> Superclass; + typedef itk::SmartPointer<Self> Pointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(StreamingGraphToImageFilter, ImageSource); + + /** Typedefs for processing */ + typedef typename TLabelImage::RegionType RegionType; + typedef typename TLabelImage::IndexType IndexType; + typedef typename TLabelImage::SizeType SizeType; + typedef typename TLabelImage::SpacingType SpacingType; + typedef typename TLabelImage::PointType PointType; + typedef typename TGraph::NodePointerType NodePointerType; + typedef itk::GrayscaleFillholeImageFilter<TLabelImage,TLabelImage> FillholeFilterType; + typedef bg::model::point<float, 2, bg::cs::cartesian> point; + typedef bg::model::box<point> box; + typedef std::pair<box, unsigned> value; + + /** Set the input graph and build the R-Tree */ + void SetGraph(const TGraph graph); + + /** Prepare image allocation at the first call of the pipeline processing */ + virtual void GenerateOutputInformation(void); + + /** Does the real work. */ + virtual void GenerateData(); + + itkSetMacro(OutputSize, SizeType); + itkSetMacro(OutputOrigin, PointType); + itkSetMacro(OutputSpacing, SpacingType); + itkSetMacro(OutputProjectionRef, std::string); + +protected: + StreamingGraphToImageFilter(){}; + virtual ~StreamingGraphToImageFilter(){}; + +private: + + StreamingGraphToImageFilter(const Self &); //purposely not implemented + void operator =(const Self&); //purposely not implemented + + TGraph m_Graph; + bgi::rtree< value, bgi::quadratic<16> > rtree; + SizeType m_OutputSize; + SpacingType m_OutputSpacing; + PointType m_OutputOrigin; + std::string m_OutputProjectionRef; +}; + +} /* namespace lsgrm */ + +#ifndef OTB_MANUAL_INSTANTIATION +#include <otbStreamingGraphToImageFilter.txx> +#endif + +#endif /* MODULES_REMOTE_LSGRM_INCLUDE_OTBSTREAMINGGRAPHTOIMAGEFILTER_H_ */ diff --git a/include/otbStreamingGraphToImageFilter.txx b/include/otbStreamingGraphToImageFilter.txx new file mode 100644 index 0000000000000000000000000000000000000000..0332f547bf63ae56813caca4989eb8f0da2d955c --- /dev/null +++ b/include/otbStreamingGraphToImageFilter.txx @@ -0,0 +1,168 @@ +/* + * otbStreamingGraphToImageFilter.txx + * + * Created on: 6 nov. 2017 + * Author: cresson + */ + +#ifndef MODULES_REMOTE_LSGRM_INCLUDE_OTBSTREAMINGGRAPHTOIMAGEFILTER_TXX_ +#define MODULES_REMOTE_LSGRM_INCLUDE_OTBSTREAMINGGRAPHTOIMAGEFILTER_TXX_ + +#include <otbStreamingGraphToImageFilter.h> + +namespace lsgrm +{ + +template <class TGraph, class TLabelImage> +void +StreamingGraphToImageFilter<TGraph, TLabelImage> +::SetGraph(const TGraph graph) + { + m_Graph = graph; + + // Build a R-Tree + itkDebugMacro( << "Building R-Tree" ); + + rtree.clear(); + unsigned int count = 0; + for(auto& node : m_Graph.m_Nodes) + { + // create a box + box b( + point( + node->m_Bbox.m_UX, + node->m_Bbox.m_UY), + point( + node->m_Bbox.m_UX + node->m_Bbox.m_W, + node->m_Bbox.m_UY + node->m_Bbox.m_H)); + + // insert new value + rtree.insert(std::make_pair(b, count)); + count++; + } + + itkDebugMacro( << "Building R-Tree finished" ); + + } + +template <class TGraph, class TLabelImage> +void +StreamingGraphToImageFilter<TGraph, TLabelImage> +::GenerateOutputInformation() + { + itkDebugMacro( << "Entering GenerateOutputInformation()" ); + + // Output Largest Possible Region + IndexType index; + index.Fill(0); + RegionType outputRegion(index, m_OutputSize); + + // Set output informations + TLabelImage * outputPtr = this->GetOutput(); + outputPtr->SetOrigin ( m_OutputOrigin ); + outputPtr->SetSpacing ( m_OutputSpacing ); + outputPtr->SetLargestPossibleRegion( outputRegion ); + outputPtr->SetProjectionRef(m_OutputProjectionRef); + + } + + +template <class TGraph, class TLabelImage> +void +StreamingGraphToImageFilter<TGraph, TLabelImage> +::GenerateData() + { + itkDebugMacro( << "Entering GenerateData()" ); + + // Allocate the output buffer + TLabelImage * outputPtr = this->GetOutput(); + RegionType outReqRegion = outputPtr->GetRequestedRegion(); + outputPtr->SetBufferedRegion(outputPtr->GetRequestedRegion()); + outputPtr->Allocate(); + + // Find nodes intersecting find the output requested region + box query_box( + point( + outReqRegion.GetIndex(0), + outReqRegion.GetIndex(1)), + point( + outReqRegion.GetIndex(0)+outReqRegion.GetSize(0), + outReqRegion.GetIndex(1)+outReqRegion.GetSize(1))); + std::vector<value> result_s; + itkDebugMacro( << "R-Tree query on output requested region " << outReqRegion ); + rtree.query(bgi::intersects(query_box), std::back_inserter(result_s)); + itkDebugMacro( << "R-Tree query done. Number of nodes: " << result_s.size() ); + + // Retrieve the bounding box of the intersecting nodes (a kind of "Input requested region") + box realBBox(query_box); + for(auto& res : result_s) + { + boost::geometry::expand(realBBox, res.first); + } + IndexType index; + index[0] = realBBox.min_corner().get<0>(); + index[1] = realBBox.min_corner().get<1>(); + SizeType size; + size[0] = realBBox.max_corner().get<0>() - realBBox.min_corner().get<0>(); + size[1] = realBBox.max_corner().get<1>() - realBBox.min_corner().get<1>(); + RegionType inputRequestedRegion(index, size); + itkDebugMacro( << "Input Requested region: " << inputRequestedRegion ); + + // Generate the label image + itkDebugMacro( << "Allocate buffered region "); + const typename TLabelImage::InternalPixelType noDataLabel = 0; + typename TLabelImage::Pointer labelImage = TLabelImage::New(); + labelImage->SetRegions(inputRequestedRegion); + labelImage->Allocate(); + labelImage->FillBuffer(noDataLabel); + itkDebugMacro( << "Allocate buffered region ok" ); + + using LabelImageIterator = itk::ImageRegionIterator<TLabelImage>; + LabelImageIterator it(labelImage, inputRequestedRegion); + + // Burn boundaries + itkDebugMacro( << "Burning boundaries " ); + for(auto& res : result_s) + { + NodePointerType node = m_Graph.m_Nodes[res.second]; + + lp::CellLists borderPixels; + lp::ContourOperations::GenerateBorderCells(borderPixels, node->m_Contour, node->m_Id, m_OutputSize[0]); + + for (auto& pix: borderPixels) + { + // patch raf (catch exception if position of pixel to set is not correct) + try { + index[0] = (unsigned int)(pix % (std::size_t)(m_OutputSize[0])); + index[1] = (unsigned int)(pix / (std::size_t)(m_OutputSize[0])); + labelImage->SetPixel(index, res.second); + } catch (std::exception e) { + std::cout << "Pixel ID: " << pix << std::endl; + std::cout << "Derived index: " << index[0] << "," << index[1] << std::endl; + std::cout << "Node position in list: " << res.second << std::endl; + } + // end patch + } + } + itkDebugMacro( << "Burning boundaries ok "); + + // Fill holes + itkDebugMacro( << "fill Hole filter" ); + typename FillholeFilterType::Pointer fillFilter = FillholeFilterType::New(); + fillFilter->SetInput(labelImage); + fillFilter->Update(); + itkDebugMacro( << "Fill Hole filter OutputLargestPossibleRegion:" << fillFilter->GetOutput()->GetLargestPossibleRegion() ); + itkDebugMacro( << "fill Hole filter ok" ); + + // Extract the stable region + LabelImageIterator outIt(outputPtr, outReqRegion); + LabelImageIterator inIt (fillFilter->GetOutput(), outReqRegion); + for (inIt.GoToBegin(), outIt.GoToBegin(); !outIt.IsAtEnd(); ++outIt, ++inIt) + outIt.Set(inIt.Get()); + } + + + +} /* namespace lsgrm */ + +#endif