Commit 9493484b authored by remicres's avatar remicres

ENH: Fully modularized! Now require GRM Module

parent bb65d2e0
......@@ -2,4 +2,4 @@ cmake_minimum_required (VERSION 2.8)
OTB_CREATE_APPLICATION(NAME LSGRM
SOURCES otbLSGRM.cxx
LINK_LIBRARIES OTBCommon)
LINK_LIBRARIES OTBCommon OTBGRM)
......@@ -49,48 +49,48 @@ private:
void DoInit()
{
SetName("GenericRegionMerging");
SetDescription("This application allows to use the Generic Region Merging library "
"(GRM) and provides currently 3 homogeneity criteria: Euclidean Distance, "
"Full Lambda Schedule and Baatz & Schape criterion.");
AddParameter(ParameterType_InputImage, "in", "Input Image");
AddParameter(ParameterType_OutputImage, "out", "Ouput Label Image");
SetDefaultOutputPixelType("out", ImagePixelType_uint32);
// AddParameter(ParameterType_Choice, "criterion", "Homogeneity criterion to use");
// AddChoice("criterion.bs", "Baatz & Schape");
// AddChoice("criterion.ed", "Euclidean Distance");
// AddChoice("criterion.fls", "Full Lambda Schedule");
AddParameter(ParameterType_Float, "threshold", "Threshold for the criterion");
AddParameter(ParameterType_Int, "niter", "Maximum number of iterations");
SetDefaultParameterInt("niter", 75);
MandatoryOff("niter");
AddParameter(ParameterType_Int, "speed", "Activate it to boost the segmentation speed");
SetDefaultParameterInt("speed", 0);
MandatoryOff("speed");
// For Baatz & Schape
AddParameter(ParameterType_Float, "cw", "Weight for the spectral homogeneity");
SetDefaultParameterFloat("cw", 0.5);
MandatoryOff("cw");
AddParameter(ParameterType_Float, "sw", "Weight for the spatial homogeneity");
SetDefaultParameterFloat("sw", 0.5);
MandatoryOff("sw");
// For large scale
AddParameter(ParameterType_Directory, "tmpdir", "temporary directory for tiles and graphs");
AddParameter(ParameterType_Choice, "tiling", "Tiling layout for the large scale segmentation");
AddChoice("tiling.auto", "Automatic tiling layout");
AddChoice("tiling.user", "User tiling layout");
AddParameter(ParameterType_Int, "tiling.user.sizex", "Tiles width");
AddParameter(ParameterType_Int, "tiling.user.sizey", "Tiles height");
AddParameter(ParameterType_Int, "tiling.user.nfirstiter", "Number of first iterations");
AddParameter(ParameterType_Int, "tiling.user.memory", "Available memory");
AddChoice("tiling.none", "No tiling layout");
SetName("GenericRegionMerging");
SetDescription("This application allows to use the Generic Region Merging library "
"(GRM) and provides currently 3 homogeneity criteria: Euclidean Distance, "
"Full Lambda Schedule and Baatz & Schape criterion.");
AddParameter(ParameterType_InputImage, "in", "Input Image");
AddParameter(ParameterType_OutputImage, "out", "Ouput Label Image");
SetDefaultOutputPixelType("out", ImagePixelType_uint32);
// AddParameter(ParameterType_Choice, "criterion", "Homogeneity criterion to use");
// AddChoice("criterion.bs", "Baatz & Schape");
// AddChoice("criterion.ed", "Euclidean Distance");
// AddChoice("criterion.fls", "Full Lambda Schedule");
AddParameter(ParameterType_Float, "threshold", "Threshold for the criterion");
AddParameter(ParameterType_Int, "niter", "Maximum number of iterations");
SetDefaultParameterInt("niter", 75);
MandatoryOff("niter");
AddParameter(ParameterType_Int, "speed", "Activate it to boost the segmentation speed");
SetDefaultParameterInt("speed", 0);
MandatoryOff("speed");
// For Baatz & Schape
AddParameter(ParameterType_Float, "cw", "Weight for the spectral homogeneity");
SetDefaultParameterFloat("cw", 0.5);
MandatoryOff("cw");
AddParameter(ParameterType_Float, "sw", "Weight for the spatial homogeneity");
SetDefaultParameterFloat("sw", 0.5);
MandatoryOff("sw");
// For large scale
AddParameter(ParameterType_Directory, "tmpdir", "temporary directory for tiles and graphs");
AddParameter(ParameterType_Choice, "tiling", "Tiling layout for the large scale segmentation");
AddChoice("tiling.auto", "Automatic tiling layout");
AddChoice("tiling.user", "User tiling layout");
AddParameter(ParameterType_Int, "tiling.user.sizex", "Tiles width");
AddParameter(ParameterType_Int, "tiling.user.sizey", "Tiles height");
AddParameter(ParameterType_Int, "tiling.user.nfirstiter", "Number of first iterations");
AddParameter(ParameterType_Int, "tiling.user.memory", "Available memory");
AddChoice("tiling.none", "No tiling layout");
}
void DoUpdateParameters()
......@@ -167,16 +167,8 @@ private:
otbAppLogFATAL("Unknow input tiling mode!");
}
// MPI configuration
#ifdef OTB_USE_MPI
if (myrank==0)
std::cout << "Number of MPI process : " << nprocs << std::endl;
controller.SetNumberOfProcess(nprocs);
controller.SetProcessRank(myrank);
#endif
// Specific parameters
lsrm::BaatzParam params;
grm::BaatzParam params;
params.m_SpectralWeight = GetParameterFloat("cw");
params.m_ShapeWeight = GetParameterFloat("sw");
controller.SetSpecificParameters(params);
......@@ -197,12 +189,26 @@ private:
labelImage->SetSpacing(inputImage->GetSpacing());
SetParameterOutputImage<UInt32ImageType>("out", labelImage);
// Get temporary files list
m_TemporaryFilesList = controller.GetTemporaryFilesList();
}
void AfterExecuteAndWriteOutputs()
{
// 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) );
}
}
}
private:
std::vector<std::string> m_TemporaryFilesList;
}; // app class
} // end of namespace wrapper
} // end of namespace otb
......
#include "lpContour.h"
namespace lp
{
void ContourOperations::MergeContour(Contour& mergedContour,
BoundingBox& mergedBBox,
Contour& contour1,
Contour& contour2,
BoundingBox& bbox1,
BoundingBox& bbox2,
const CellIndex cid1,
const CellIndex cid2,
const std::size_t gridSizeX)
{
// First step consists of building the bounding box resulting from the fusion
// of the bounding boxes bbox1 and bbox2
mergedBBox = MergeBoundingBoxes(bbox1, bbox2);
// Create the associate matrix indicating where the cells are located
// inside the bbox
std::vector<bool> cellMatrix(mergedBBox.m_W*mergedBBox.m_H, false);
// Fill the cell matrix with the cells from both contours
{
CellLists borderCells;
// Fill with the cells of contour 1
GenerateBorderCells(borderCells, contour1, cid1, gridSizeX);
for(auto& cell: borderCells)
cellMatrix[GridToBBox(cell, mergedBBox, gridSizeX)] = true;
borderCells.clear();
// Fill with the cells of contour 2
GenerateBorderCells(borderCells, contour2, cid2, gridSizeX);
for(auto& cell: borderCells)
cellMatrix[GridToBBox(cell, mergedBBox, gridSizeX)] = true;
}
// Create the new contour
CreateNewContour(mergedContour, GridToBBox(cid1, mergedBBox, gridSizeX), cellMatrix, mergedBBox.m_W, mergedBBox.m_H);
}
void ContourOperations::CreateNewContour(Contour& newContour,
const CellIndex cidx,
const std::vector<bool>& cellMatrix,
const std::size_t bboxWidth,
const std::size_t bboxHeight)
{
// The first move is always 1
Push1(newContour);
// Previous move
short prevMove = 1;
// Local pixel id
CellIndex currIdx = cidx;
// Table containing id neighbors
long int neighbors[8];
for(;;)
{
// Compute neighbor'ids
EIGHTNeighborhood(neighbors, currIdx, bboxWidth, bboxHeight);
if(prevMove == 1)
{
if(neighbors[1] != -1 && cellMatrix[neighbors[1]])
{
Push0(newContour);
currIdx = currIdx + 1 - bboxWidth;
prevMove = 0;
}
else if(neighbors[2] != -1 && cellMatrix[neighbors[2]])
{
Push1(newContour);
currIdx++;
prevMove = 1;
}
else
{
Push2(newContour);
prevMove = 2;
}
}
else if(prevMove == 2)
{
if(neighbors[3] != -1 && cellMatrix[neighbors[3]])
{
Push1(newContour);
currIdx = currIdx + bboxWidth + 1;
prevMove = 1;
}
else if(neighbors[4] != -1 && cellMatrix[neighbors[4]])
{
Push2(newContour);
currIdx += bboxWidth;
prevMove = 2;
}
else
{
Push3(newContour);
prevMove = 3;
}
}
else if(prevMove == 3)
{
if(neighbors[5] != -1 && cellMatrix[neighbors[5]])
{
Push2(newContour);
currIdx = currIdx - 1 + bboxWidth;
prevMove = 2;
}
else if(neighbors[6] != -1 && cellMatrix[neighbors[6]])
{
Push3(newContour);
currIdx -= 1;
prevMove = 3;
}
else
{
Push0(newContour);
prevMove = 0;
}
}
else
{
assert(prevMove == 0);
if(neighbors[7] != -1 && cellMatrix[neighbors[7]])
{
Push3(newContour);
currIdx = currIdx - bboxWidth - 1;
prevMove = 3;
}
else if(neighbors[0] != -1 && cellMatrix[neighbors[0]])
{
Push0(newContour);
currIdx -= bboxWidth;
prevMove = 0;
}
else
{
if(currIdx == cidx)
break;
else
{
Push1(newContour);
prevMove = 1;
}
}
}
}
}
void ContourOperations::GenerateBorderCells(CellLists& borderCells,
const Contour& contour,
const CellIndex startCellId,
const std::size_t gridSizeX)
{
// Add the first pixel to the border list
borderCells.insert(startCellId);
if(contour.size() > 8)
{
// Intialize the first move at prev
short curr, prev = GetMove10(GetMove2(0, contour));
// Declare the current pixel index
CellIndex idx = startCellId;
// Explore the contour
for(ContourIndex cidx = 1; cidx < contour.size() / 2; cidx++)
{
curr = GetMove10(GetMove2(cidx, contour));
assert(curr >= 0 && curr < 4);
if(curr == 0)
{
// Impossible case is prev = 2;
assert(prev != 2);
//*
//*
if(prev == 0)
{
idx -= gridSizeX;
borderCells.insert(idx);
}
// *
// **
if(prev == 1)
{
idx = idx + 1 - gridSizeX;
borderCells.insert(idx);
}
}else if(curr == 1)
{
// Impossible case
assert(prev != 3);
// **
if(prev == 1)
{
idx++;
borderCells.insert(idx);
}
//*
//**
if (prev == 2)
{
idx = idx + 1 + gridSizeX;
borderCells.insert(idx);
}
}else if(curr == 2)
{
// Impossible case
assert(prev != 0);
// *
// *
if(prev == 2)
{
idx += gridSizeX;
borderCells.insert(idx);
}
// **
// *
if(prev == 3)
{
idx = idx - 1 + gridSizeX;
borderCells.insert(idx);
}
}else
{
// Impossible case
assert(prev != 1);
if(prev == 0)
{
idx = idx - 1 - gridSizeX;
borderCells.insert(idx);
}
if(prev == 3)
{
idx--;
borderCells.insert(idx);
}
}
prev = curr;
}
}
}
CellIndex ContourOperations::BBoxToGrid(const CellIndex bboxId,
const BoundingBox& bbox,
const std::size_t gridSizeX)
{
CellIndex bbX = bboxId % bbox.m_W;
CellIndex bbY = bboxId / bbox.m_W;
CellIndex gridX = bbox.m_UX + bbX;
CellIndex gridY = bbox.m_UY + bbY;
CellIndex gridId = gridY * gridSizeX + gridX;
return gridId;
}
CellIndex ContourOperations::GridToBBox(const CellIndex gridId,
const BoundingBox& bbox,
const std::size_t gridSizeX)
{
CellIndex gridX = gridId % gridSizeX;
CellIndex gridY = gridId / gridSizeX;
CellIndex bbX = gridX - bbox.m_UX;
CellIndex bbY = gridY - bbox.m_UY;
CellIndex bbId = bbY * bbox.m_W + bbX;
return bbId;
}
void ContourOperations::EIGHTNeighborhood(long int * neighborhood,
const CellIndex id,
const std::size_t width,
const std::size_t height)
{
const unsigned int x = id % width;
const unsigned int y = id / width;
/* top */
neighborhood[0] = ( y > 0 ? (id - width) : -1 );
/* top right */
neighborhood[1] = ( (y > 0 && x < (width - 1) ) ? (id - width + 1) : -1 );
/* right */
neighborhood[2] = ( x < (width - 1) ? (id + 1) : -1 );
/* bottom right */
neighborhood[3] = ( (x < (width - 1) && y < (height - 1) ) ? (id + 1 + width) : -1);
/* bottom */
neighborhood[4] = ( y < (height - 1) ? (id + width) : -1 );
/* bottom left */
neighborhood[5] = ( (y < (height - 1) && x > 0) ? (id + width - 1) : -1 );
/* left */
neighborhood[6] = ( x > 0 ? (id - 1) : -1 );
/* top left */
neighborhood[7] = ( (x > 0 && y > 0) ? (id -width - 1) : - 1);
}
BoundingBox ContourOperations::MergeBoundingBoxes(const BoundingBox& bb1,
const BoundingBox& bb2)
{
std::size_t min_ux, min_uy, max_xw, max_yh;
BoundingBox bb;
min_ux = std::min(bb1.m_UX, bb2.m_UX);
min_uy = std::min(bb1.m_UY, bb2.m_UY);
max_xw = std::max(bb1.m_UX + bb1.m_W, bb2.m_UX + bb2.m_W);
max_yh = std::max(bb1.m_UY + bb1.m_H, bb2.m_UY + bb2.m_H);
bb.m_UX = min_ux;
bb.m_UY = min_uy;
bb.m_W = max_xw - min_ux;
bb.m_H = max_yh - min_uy;
return bb;
}
Move ContourOperations::GetMove2(ContourIndex idx, const Contour& contour)
{
return Move(2*contour[2*idx] + contour[idx*2 +1]);
}
short ContourOperations::GetMove10(const Move& m)
{
return (m[0]*2 + m[1]);
}
void ContourOperations::Push0(Contour& contour)
{
contour.push_back(0); contour.push_back(0);
}
void ContourOperations::Push1(Contour& contour)
{
contour.push_back(1); contour.push_back(0);
}
void ContourOperations::Push2(Contour& contour)
{
contour.push_back(0); contour.push_back(1);
}
void ContourOperations::Push3(Contour& contour)
{
contour.push_back(1); contour.push_back(1);
}
} // end of namespace lp
#ifndef __LP_CONTOUR_H
#define __LP_CONTOUR_H
#include <cassert>
#include <iostream>
#include <bitset>
#include <vector>
#include <utility>
#include <unordered_set>
#include <unordered_map>
#include <boost/dynamic_bitset.hpp>
namespace lp
{
/* Move along a contour: 4 possibles: top (0), right(1), bottom (2), left(3). */
using Move = std::bitset<2>;
/* A contour is a list of moves represented by a pair of bits*/
using Contour = boost::dynamic_bitset<>;
using ContourIndex = boost::dynamic_bitset<>::size_type;
/* Index of a cell in the grid */
using CellIndex = std::size_t;
/* List of cell indices */
using CellLists = std::unordered_set<CellIndex>;
struct BoundingBox
{
std::size_t m_UX;
std::size_t m_UY;
std::size_t m_W;
std::size_t m_H;
};
class ContourOperations
{
public:
/* Methods to fill a contour. */
static void Push0(Contour& contour); // Push a move to the top.
static void Push1(Contour& contour); // Push a move to the right.
static void Push2(Contour& contour); // Push a move to the bottom.
static void Push3(Contour& contour); // Push a move to the left.
/* Methods to access to elements of a contour. */
static Move GetMove2(ContourIndex idx, const Contour& contour); // Retrieve a move
static short GetMove10(const Move& m); // Transform the move into a 10 base number (0,1,2 or 3).
/* Main methods */
static void MergeContour(Contour& mergedContour,
BoundingBox& mergedBoundingBox,
Contour& contour1,
Contour& contour2,
BoundingBox& bbox1,
BoundingBox& bbox2,
const CellIndex cid1,
const CellIndex cid2,
const std::size_t gridSizeX);
static void GenerateBorderCells(CellLists& borderCells, // From a contour, the smallest id of the cell
const Contour& contour, // belonging to the shape and the size of the grid,
const CellIndex startCellId, // this method returns the ids of the border cells
const std::size_t gridSizeX); // of the shape.
static void CreateNewContour(Contour& newContour,
const CellIndex startCellId,
const std::vector<bool>& cellMatrix,
const std::size_t bboxWidth,
const std::size_t bboxHeight);
/* Utilities methods */
static BoundingBox MergeBoundingBoxes(const BoundingBox& bb1, // Given 2 bounding boxes return the bounding
const BoundingBox& bb2); // which is the union.
static void EIGHTNeighborhood(long int * neighborhood, // return 8-neighbors of cell id.
const CellIndex id,
const std::size_t width,
const std::size_t height);
static CellIndex BBoxToGrid(const CellIndex bboxId,
const BoundingBox& bbox,
const std::size_t gridSizeX);
static CellIndex GridToBBox(const CellIndex gridId, // Return the coordinates of the cell
const BoundingBox& bbox, // in the bounding box reference.
const std::size_t gridSizeX);
};
} // end of namespace lp
#include "lpContour.cpp"
#endif
......@@ -47,6 +47,7 @@ public:
void SetTilingModeAuto(){m_TilingMode = LSGRM_TILING_AUTO;};
typename LabelImageType::Pointer GetLabeledClusteredOutput();
std::vector<std::string> GetTemporaryFilesList();
itkGetMacro(Margin, unsigned int);
......
......@@ -77,8 +77,10 @@ void Controller<TSegmenter>::RunSegmentation()
// Update the given number of iterations
numberOfIterationsRemaining -= m_NumberOfFirstIterations;
#ifdef OTB_USE_MPI
// Gathering useful variables
GatherUsefulVariables(accumulatedMemory, isFusion);
#endif
// Time monitoring
ShowTime(t);
......@@ -104,9 +106,10 @@ void Controller<TSegmenter>::RunSegmentation()
// Update the given number of iterations
numberOfIterationsRemaining -= numberOfIterationsForPartialSegmentations;
#ifdef OTB_USE_MPI
// Gathering useful variables
GatherUsefulVariables(accumulatedMemory, isFusion);
#endif
// Time monitoring
ShowTime(t);
}
......@@ -131,7 +134,7 @@ void Controller<TSegmenter>::RunSegmentation()
m_InputImage->GetNumberOfComponentsPerPixel(),
numberOfIterationsRemaining);
ShowTime(t);
// ShowTime(t);
}
else // accumulatedMemory > m_Memory
......@@ -156,7 +159,7 @@ void Controller<TSegmenter>::RunSegmentation()
template<class TSegmenter>
unsigned int Controller<TSegmenter>::GetNodeMemory()
{
// Create a unique node
// Create a n*n image
const unsigned int n = 100;
typename ImageType::Pointer onePixelImage = ImageType::New();
typename ImageType::IndexType start;
......@@ -167,12 +170,17 @@ unsigned int Controller<TSegmenter>::GetNodeMemory()
onePixelImage->SetRegions(region);
onePixelImage->SetNumberOfComponentsPerPixel(m_InputImage->GetNumberOfComponentsPerPixel());
onePixelImage->Allocate();
// Instanciate and initialize a segmenter
TSegmenter segmenter;
segmenter.SetInput(onePixelImage);
lsrm::GraphOperations<TSegmenter>::InitNodes(onePixelImage,segmenter,FOUR);
grm::GraphOperations<TSegmenter>::InitNodes(onePixelImage,segmenter,FOUR);
// Get the memory occupied by the graph, normalize it by n*n
unsigned int memory = segmenter.GetGraphMemory() / (n*n);
itkDebugMacro(<<"Size of a node is " << memory);
return memory;
}
......@@ -268,8 +276,8 @@ void Controller<TSegmenter>::GetAutomaticConfiguration()
{
#ifdef OTB_USE_MPI
// We want number of tiles which is a multiple of the number of MPI processes
if (nbOfTiles % layoutNCol == 0 && // Is it a multiple of the nb of ...Tiles?
nbOfTiles % otb::MPIConfig::Instance()->GetNbProcs() == 0) // ...nProcs?
if (nbOfTiles % layoutNCol == 0 && // Is it a multiple of the nb of Tiles and nProcs?
nbOfTiles % otb::MPIConfig::Instance()->GetNbProcs() == 0)
#else
if (nbOfTiles % layoutNCol == 0) // Is it a multiple of the nb of Tiles?
#endif
......@@ -358,4 +366,19 @@ Controller<TSegmenter>::GetLabeledClusteredOutput()
{
return m_LabelImage;
}
template <class TSegmenter>
std::vector<std::string> Controller<TSegmenter>::GetTemporaryFilesList()
{
std::vector<std::string> list;
for (unsigned int i = 0; i < m_Tiles.size(); i++)
{
list.push_back(m_Tiles[i].edgeFileName);
list.push_back(m_Tiles[i].edgeMarginFileName);
list.push_back(m_Tiles[i].nodeFileName);
list.push_back(m_Tiles[i].nodeMarginFileName);
}
return list;
}
} // end of namespace lsgrm
......@@ -2,7 +2,7 @@
#define __LSGRM_GRAPH_OPERATIONS_H
#include "lsgrmHeader.h"
#include "lsrmGraphOperations.h"
#include "grmGraphOperations.h"
#include "otbVectorImage.h"
#include "otbMultiChannelExtractROI.h"
......
......@@ -93,10 +93,10 @@ MergeAllGraphsAndAchieveSegmentation(
segmenter.SetNumberOfComponentsPerPixel(imageBands);
segmenter.SetParam(params);