An error occurred while loading the file. Please try again.
-
SPeillet authorede364db2f
#include "lsgrmController.h"
namespace lsgrm
{
template<class TSegmenter>
Controller<TSegmenter>::Controller()
{
m_TilingMode = LSGRM_TILING_AUTO;
m_Margin = 0;
m_NumberOfIterations = 0;
m_NumberOfFirstIterations = 0;
m_TileHeight = 0;
m_TileWidth = 0;
m_NbTilesX = 0;
m_NbTilesY = 0;
m_Threshold = 75;
m_Memory = 0;
}
template<class TSegmenter>
Controller<TSegmenter>::~Controller()
{
}
template<class TSegmenter>
void Controller<TSegmenter>::Modified()
{
Superclass::Modified();
m_Tiles.clear();
}
/*
* Run the segmentation
* TODO: compute the correct number of iterations !
*/
template<class TSegmenter>
void Controller<TSegmenter>::RunSegmentation()
{
itkDebugMacro(<< "Entering RunSegmentation()");
if (m_TilingMode == LSGRM_TILING_AUTO || m_TilingMode == LSGRM_TILING_USER)
{
if(m_TilingMode == LSGRM_TILING_AUTO)
{
this->GetAutomaticConfiguration();
}
else if (m_TilingMode == LSGRM_TILING_USER)
{
m_NbTilesX = std::floor(m_InputImage->GetLargestPossibleRegion().GetSize()[0] / m_TileWidth);
m_NbTilesY = std::floor(m_InputImage->GetLargestPossibleRegion().GetSize()[1] / m_TileHeight);
m_Margin = static_cast<unsigned int>(pow(2, m_NumberOfFirstIterations + 1) - 2);
}
std::cout <<
"--- Configuration: " <<
"\n\tAvailable RAM: " << m_Memory <<
"\n\tInput image dimensions: " << m_InputImage->GetLargestPossibleRegion().GetSize() <<
"\n\tNumber of first iterations: " << m_NumberOfFirstIterations <<
"\n\tStability margin: " << m_Margin <<
"\n\tRegular tile size: " << m_TileWidth << " x " << m_TileHeight <<
"\n\tTiling layout: " << m_NbTilesX << " x " << m_NbTilesY << std::endl;
// Compute the splitting scheme
m_Tiles = SplitOTBImage<ImageType>(m_InputImage, m_TileWidth, m_TileHeight, m_Margin,
m_NbTilesX, m_NbTilesY, m_TemporaryFilesPrefix);
// If there is only one tile, then fallback to LSGRM_TILING_NONE case
if (m_Tiles.size() == 1)
{
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
std::cout << "Only one tile is needed. Fallback to tiling=none." << std::endl;
SetTilingModeNone();
}
}
if (m_TilingMode == LSGRM_TILING_AUTO || m_TilingMode == LSGRM_TILING_USER)
{
unsigned int numberOfIterationsForPartialSegmentations = 3; // TODO: find a smart value
unsigned int numberOfIterationsRemaining = m_NumberOfIterations;
// Boolean indicating if there are remaining fusions
bool isFusion = false;
// Run first partial segmentation
boost::timer t; t.restart();
auto accumulatedMemory = RunFirstPartialSegmentation<TSegmenter>(
m_InputImage,
m_SpecificParameters,
m_Threshold,
m_NumberOfFirstIterations,
numberOfIterationsForPartialSegmentations,
m_Tiles,
m_NbTilesX,
m_NbTilesY,
m_TileWidth,
m_TileHeight,
isFusion);
#ifdef OTB_USE_MPI
// Gathering useful variables
GatherUsefulVariables(accumulatedMemory, isFusion);
#endif
// Time monitoring
ShowTime(t);
while(accumulatedMemory > m_Memory && isFusion)
{
isFusion = false;
accumulatedMemory = RunPartialSegmentation<TSegmenter>(
m_SpecificParameters,
m_Threshold,
numberOfIterationsForPartialSegmentations,
m_Tiles,
m_NbTilesX,
m_NbTilesY,
m_InputImage->GetLargestPossibleRegion().GetSize()[0],
m_InputImage->GetLargestPossibleRegion().GetSize()[1],
m_InputImage->GetNumberOfComponentsPerPixel(),
isFusion);
std::cout << "accumulatedMemory=" << accumulatedMemory << std::endl;
#ifdef OTB_USE_MPI
// Gathering useful variables
GatherUsefulVariables(accumulatedMemory, isFusion);
#endif
// Time monitoring
ShowTime(t);
// Update number of remaining iterations
if (numberOfIterationsRemaining < numberOfIterationsForPartialSegmentations)
{
break;
}
else
{
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
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
if (otb::MPIConfig::Instance()->GetMyRank() != 0)
return;
#endif
if(accumulatedMemory <= m_Memory)
{
// Merge all the graphs
m_LabelImage = MergeAllGraphsAndAchieveSegmentation<TSegmenter>(
m_SpecificParameters,
m_Threshold,
m_Tiles,
m_NbTilesX,
m_NbTilesY,
m_InputImage->GetLargestPossibleRegion().GetSize()[0],
m_InputImage->GetLargestPossibleRegion().GetSize()[1],
m_InputImage->GetNumberOfComponentsPerPixel(),
numberOfIterationsRemaining);
ShowTime(t);
}
else // accumulatedMemory > m_Memory
{
// That means there are no more possible fusions but we can not store the output graph
// Todo do not clean up temporary directory before copying resulting graph to the output directory
// In the output directory add an info file to give the number of tiles.
itkExceptionMacro(<< "No more possible fusions, but can not store the output graph");
}
}
else if (m_TilingMode == LSGRM_TILING_NONE)// tiling_mode is none
{
#ifdef OTB_USE_MPI
// Only the master process is doing the next part
if (otb::MPIConfig::Instance()->GetMyRank() > 0)
return;
else
// Warn that there is some unused MPI processes
if (otb::MPIConfig::Instance()->GetNbProcs() > 1)
itkWarningMacro(<< "Only 1 MPI process will be used");
#endif
// Update input image
m_InputImage->Update();
// Use classic grm
TSegmenter segmenter;
segmenter.SetParam(m_SpecificParameters);
segmenter.SetThreshold(m_Threshold);
segmenter.SetDoFastSegmentation(false);
segmenter.SetNumberOfIterations(m_NumberOfIterations);
segmenter.SetInput(m_InputImage);
segmenter.Update();
// Get label image
m_LabelImage = segmenter.GetLabeledClusteredOutput();
}
else
{
itkExceptionMacro(<<"Unknow tiling mode!");
}
}
/*
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
* Compute the memory occupied by one node
* TODO: compute the exact value, e.g. experimental measures shows that
* for one Baatz node (+pixel) memory is about 700-730 bytes...
* And our estimation is of 456!
*/
template<class TSegmenter>
unsigned int Controller<TSegmenter>::GetNodeMemory()
{
// Create a n*n image
const unsigned int n = 100;
typename ImageType::Pointer onePixelImage = ImageType::New();
typename ImageType::IndexType start;
start.Fill(0);
typename ImageType::SizeType size;
size.Fill(n);
typename ImageType::RegionType region(start, size);
onePixelImage->SetRegions(region);
onePixelImage->SetNumberOfComponentsPerPixel(m_InputImage->GetNumberOfComponentsPerPixel());
onePixelImage->Allocate();
// Instanciate and initialize a segmenter
TSegmenter segmenter;
segmenter.SetInput(onePixelImage);
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);
itkWarningMacro(<<"Size of a node is " << memory);
// Get the memory occupied by one pixel of the image
unsigned int pixelMemory = sizeof(m_InputImage->GetBufferPointer())
* m_InputImage->GetNumberOfComponentsPerPixel();
itkWarningMacro(<<"Size of an image pixel is " << pixelMemory);
memory += pixelMemory;
itkWarningMacro(<<"Size of a node+pixel is " << memory);
return memory;
}
/*
* Compute the maximum number of nodes which can fit in the system memory
*/
template<class TSegmenter>
long unsigned int Controller<TSegmenter>::GetMaximumNumberOfNodesInMemory()
{
itkDebugMacro(<< "Computing maximum number of nodes in memory");
m_Memory = getMemorySize();
assert(m_Memory > 0);
m_Memory /= 2; // For safety and can prevent out of memory troubles
return std::ceil(((float) m_Memory) / ((float) GetNodeMemory()));
}
template<class TSegmenter>
void Controller<TSegmenter>::ComputeMaximumStabilityMargin(unsigned int width,
unsigned int height, unsigned int &niter, unsigned int &margin)
{
itkDebugMacro(<< "Computing maximum stability margin");
// Compute the stability margin. The naive strategy consider a margin value and a stable size equal.
niter = 1;
unsigned int maxMargin = std::min(width, height)/2;
unsigned int currMargin = static_cast<unsigned int>(pow(2, niter + 1) - 2);
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
margin = currMargin;
while(currMargin < maxMargin)
{
margin = currMargin;
niter++;
currMargin = static_cast<unsigned int>(pow(2, niter + 1) - 2);
}
niter--;
itkDebugMacro(<< "Number of iterations=" << niter << " margin=" << margin);
}
/*
* Compute a tiling layout which minimizes a criterion based on tile compactness
* and memory usage
*
* TODO: use the lsgrmSplitter to truly compute the largest tile of a given layout
*/
template<class TSegmenter>
void Controller<TSegmenter>::GetAutomaticConfiguration()
{
itkDebugMacro(<<"Get automatic configuration");
// Compute the maximum number of nodes that can fit the memory
// TODO: Use the smallest number amongst MPI processes
unsigned long int maximumNumberOfNodesInMemory = GetMaximumNumberOfNodesInMemory();
itkDebugMacro(<<"Maximum number of nodes in memory is " << maximumNumberOfNodesInMemory);
// Number of nodes in the entire image
const unsigned int imageWidth = m_InputImage->GetLargestPossibleRegion().GetSize()[0];
const unsigned int imageHeight = m_InputImage->GetLargestPossibleRegion().GetSize()[1];
const unsigned long int nbOfNodesInImage = imageWidth*imageHeight;
// Default layout: 1x1
m_NbTilesX = 1;
m_NbTilesY = 1;
// Without margins, the number of tiles maximizing memory use
// is equal to: nbOfNodesInImage / maximumNumberOfNodesInMemory.
// Actually, there is tile margins. And the best scenario is to have
// square tiles with margin = width/2, that is tiles 4x larger.
// Hence the number of tiles maximizing memory use is 4x larger.
unsigned int minimumNumberOfTiles = std::ceil(4 * nbOfNodesInImage / ((float) maximumNumberOfNodesInMemory));
itkDebugMacro(<<"Minimum number of tiles is " << minimumNumberOfTiles);
// In the following steps, we will optimize tiling layout, starting from a number
// of tiles equal to "minimumNumberOfTiles", up to a number of tiles equal to
// twice the number of tiles (that is memory usage about 50%)
unsigned int maximumNumberOfTiles = minimumNumberOfTiles * 4;
// Search for layout which minimizes the criterion
// The criterion is the ratio between compactness and memory usage
// (i.e. tileWidth * tileHeight / maximumNumberOfNodesInMemory)
itkDebugMacro(<<"Computing layouts properties:");
float lowestCriterionValue = itk::NumericTraits<float>::max();
for (unsigned int nbOfTiles = minimumNumberOfTiles ; nbOfTiles <= maximumNumberOfTiles ; nbOfTiles++)
{
// Get the multiples of k. For each one, compute the criterion of the tiling
for (unsigned int layoutNCol = 1; layoutNCol<=nbOfTiles; layoutNCol++)
{
#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 and nProcs?
nbOfTiles % otb::MPIConfig::Instance()->GetNbProcs() == 0)
#else
if (nbOfTiles % layoutNCol == 0) // Is it a multiple of the nb of Tiles?
#endif
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
{
// Tiling layout
unsigned int layoutNRow = nbOfTiles / layoutNCol;
unsigned int tileWidth = imageWidth / layoutNCol;
unsigned int tileHeight = imageHeight / layoutNRow;
// Compute margin for regular tiles of this layout
unsigned int maxMargin, maxIter;
ComputeMaximumStabilityMargin(tileWidth, tileHeight, maxIter, maxMargin);
tileWidth += 2*maxMargin;
tileHeight += 2*maxMargin;
// Memory use efficiency
float percentMemory = tileWidth * tileHeight / (float) maximumNumberOfNodesInMemory; // ]0, 1]
// Compactness
float perimeter = tileWidth + tileHeight;
float surface = tileWidth * tileHeight;
float compactness = perimeter / surface * (float) std::max(tileWidth,tileHeight); // [1,+inf]
// Update minimum criterion
float criterion = compactness / percentMemory; // ]0, +inf]
itkDebugMacro(<< std::setprecision (2) << std::fixed
<< "Nb. tiles=" << nbOfTiles
<< " Layout: " << layoutNRow << "x" << layoutNCol
<< " Mem. use=" << percentMemory
<< " Compactness=" << compactness
<< " Criterion=" << criterion
<< " Size (no margin): " << (tileWidth-2*maxMargin)<< "x"<< (tileHeight-2*maxMargin)
<< " Size (with margin): " << tileWidth << "x" << tileHeight
<< " (margin=" << maxMargin << "/nb. iter=" << maxIter << ")" );
if (criterion < lowestCriterionValue)
{
lowestCriterionValue = criterion;
m_NbTilesX = layoutNCol;
m_NbTilesY = layoutNRow;
}
}
} // for each multiple of k
}
// Compute the tile size
m_TileWidth = static_cast<unsigned int>(imageWidth/m_NbTilesX);
m_TileHeight = static_cast<unsigned int>(imageHeight/m_NbTilesY);
itkDebugMacro(<<"Selected layout: " << m_NbTilesX << "x" << m_NbTilesY
<< " (criterion=" << lowestCriterionValue << ")");
// Compute the stability margin
ComputeMaximumStabilityMargin(m_TileWidth, m_TileHeight,m_NumberOfFirstIterations, m_Margin);
long long unsigned int memoryUsed = GetNodeMemory();
memoryUsed *= static_cast<long long unsigned int>(m_TileHeight + 2*m_Margin);
memoryUsed *= static_cast<long long unsigned int>(m_TileWidth + 2*m_Margin);
itkDebugMacro(<< "An amount of " << memoryUsed/(1024.0*1024.0) << " Mbytes of RAM will be used for regular tiles of size "
<< (m_TileWidth + 2*m_Margin) << "x" << (m_TileHeight + 2*m_Margin) );
}
template <class TSegmenter>
void Controller<TSegmenter>::SetInternalMemoryAvailable(long long unsigned int v) // expecting a value in Mbytes.
{
assert(v > 0);
m_Memory = v * 1024ul * 1024ul;
}
template<class TSegmenter>
void Controller<TSegmenter>::SetInputImage(ImageType * inputImage)
{
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
m_InputImage = inputImage;
}
template<class TSegmenter>
void Controller<TSegmenter>::SetSpecificParameters(const SegmentationParameterType& params)
{
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>
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