Commit 619d9964 authored by remicres's avatar remicres
Browse files

ENH: Improve automatic tiling algo

parent 135fc8bd
...@@ -15,9 +15,9 @@ public: ...@@ -15,9 +15,9 @@ public:
/** Standard class typedef */ /** Standard class typedef */
typedef Controller Self; typedef Controller Self;
typedef itk::LightObject Superclass; typedef itk::Object Superclass;
typedef itk::SmartPointer<Self> Pointer; typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer; typedef itk::SmartPointer<const Self> ConstPointer;
/** Runtime information support. */ /** Runtime information support. */
itkTypeMacro(Controller, itk::Object); itkTypeMacro(Controller, itk::Object);
...@@ -25,14 +25,6 @@ public: ...@@ -25,14 +25,6 @@ public:
/** Method for creation through the object factory. */ /** Method for creation through the object factory. */
itkNewMacro(Self); itkNewMacro(Self);
/** Enum for tiling mode */
enum LSGRMTilingMode
{
LSGRM_TILING_NONE,
LSGRM_TILING_USER,
LSGRM_TILING_AUTO
};
/* Some convenient typedefs */ /* Some convenient typedefs */
using SegmenterType = TSegmenter; using SegmenterType = TSegmenter;
using ImageType = typename SegmenterType::ImageType; using ImageType = typename SegmenterType::ImageType;
...@@ -88,7 +80,16 @@ public: ...@@ -88,7 +80,16 @@ public:
private: private:
/** Enum for tiling mode */
enum LSGRMTilingMode
{
LSGRM_TILING_NONE, // No tiling
LSGRM_TILING_USER, // User tiling
LSGRM_TILING_AUTO // Automatic tiling
};
void GetAutomaticConfiguration(); void GetAutomaticConfiguration();
void ComputeMaximumStabilityMargin(unsigned int width, unsigned int height, unsigned int &iter, unsigned int &margin);
long unsigned int GetMaximumNumberOfNodesInMemory(); long unsigned int GetMaximumNumberOfNodesInMemory();
/* Parameters given by the user */ /* Parameters given by the user */
...@@ -104,19 +105,19 @@ private: ...@@ -104,19 +105,19 @@ private:
/* Parameters given by the user or computed automatically /* Parameters given by the user or computed automatically
* depending of the chosen mode * depending of the chosen mode
*/ */
long long unsigned int m_Memory; // RAM available for the computation. long long unsigned int m_Memory; // RAM available for the computation.
unsigned int m_NbTilesX; unsigned int m_NbTilesX; // number of tiles in x dimension
unsigned int m_NbTilesY; unsigned int m_NbTilesY; // number of tiles in y dimension
unsigned int m_NumberOfFirstIterations; unsigned int m_NumberOfFirstIterations; // number of iterations in the first step
unsigned int m_TileWidth; unsigned int m_TileWidth; // regular tile width (i.e. not left tiles)
unsigned int m_TileHeight; unsigned int m_TileHeight; // regular tile height (i.e. not bottom tiles)
/* read-only variables */ /* read-only variables */
unsigned int m_Margin; LSGRMTilingMode m_TilingMode; // tiling mode (none/user/auto)
std::vector<ProcessingTile> m_Tiles; unsigned int m_Margin; // stability margin related to m_NumberOfFirstIterations
std::vector<std::string> m_TemporaryFilesList; std::vector<ProcessingTile> m_Tiles; // list of tiles
LSGRMTilingMode m_TilingMode; std::vector<std::string> m_TemporaryFilesList; // list of temporary files
typename LabelImageType::Pointer m_LabelImage; typename LabelImageType::Pointer m_LabelImage; // output label image
}; };
} // end of namespace lsgrm } // end of namespace lsgrm
......
...@@ -7,6 +7,16 @@ template<class TSegmenter> ...@@ -7,6 +7,16 @@ template<class TSegmenter>
Controller<TSegmenter>::Controller() Controller<TSegmenter>::Controller()
{ {
m_TilingMode = LSGRM_TILING_AUTO; 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_CleanTemporaryDirectory = true;
m_Threshold = 75;
m_Memory = 0;
} }
template<class TSegmenter> template<class TSegmenter>
...@@ -20,9 +30,9 @@ void Controller<TSegmenter>::RunSegmentation() ...@@ -20,9 +30,9 @@ void Controller<TSegmenter>::RunSegmentation()
if (m_TilingMode == LSGRM_TILING_AUTO || m_TilingMode == LSGRM_TILING_USER) if (m_TilingMode == LSGRM_TILING_AUTO || m_TilingMode == LSGRM_TILING_USER)
{ {
// TODO: smarter value?
const unsigned int numberOfIterationsForPartialSegmentations = 3; const unsigned int numberOfIterationsForPartialSegmentations = 3; // TODO: find a smart value
unsigned int numberOfIterations = m_NumberOfIterations; unsigned int numberOfIterationsRemaining = m_NumberOfIterations;
if(m_TilingMode == LSGRM_TILING_AUTO) if(m_TilingMode == LSGRM_TILING_AUTO)
{ {
...@@ -34,12 +44,13 @@ void Controller<TSegmenter>::RunSegmentation() ...@@ -34,12 +44,13 @@ void Controller<TSegmenter>::RunSegmentation()
} }
std::cout << std::cout <<
"--- Configuration: \n" << "--- Configuration: " <<
"\tAvailable RAM: " << m_Memory << "\n" << "\n\tAvailable RAM: " << m_Memory <<
"\tInput image dimensions: " << m_InputImage->GetLargestPossibleRegion().GetSize() << "\n" << "\n\tInput image dimensions: " << m_InputImage->GetLargestPossibleRegion().GetSize() <<
"\tNumber of first iterations: " << m_NumberOfFirstIterations << "\n" << "\n\tNumber of first iterations: " << m_NumberOfFirstIterations <<
"\tStability margin: " << m_Margin << "\n" << "\n\tStability margin: " << m_Margin <<
"\tComputed tile size: " << m_TileWidth << " x " << m_TileHeight << std::endl; "\n\tRegular tile size: " << m_TileWidth << " x " << m_TileHeight <<
"\n\tTiling layout: " << m_NbTilesX << " x " << m_NbTilesY << std::endl;
// Compute the splitting scheme // Compute the splitting scheme
SplitOTBImage<ImageType>(m_InputImage, m_TileWidth, m_TileHeight, m_Margin, SplitOTBImage<ImageType>(m_InputImage, m_TileWidth, m_TileHeight, m_Margin,
...@@ -63,9 +74,10 @@ void Controller<TSegmenter>::RunSegmentation() ...@@ -63,9 +74,10 @@ void Controller<TSegmenter>::RunSegmentation()
m_TileHeight, m_TileHeight,
m_TemporaryDirectory, m_TemporaryDirectory,
isFusion); isFusion);
// long long unsigned int accumulatedMemory = 19373051714;
// Update the given number of iterations // Update the given number of iterations
numberOfIterations -= m_NumberOfFirstIterations; numberOfIterationsRemaining -= m_NumberOfFirstIterations;
// Gathering useful variables // Gathering useful variables
GatherUsefulVariables(accumulatedMemory, isFusion); GatherUsefulVariables(accumulatedMemory, isFusion);
...@@ -73,6 +85,8 @@ void Controller<TSegmenter>::RunSegmentation() ...@@ -73,6 +85,8 @@ void Controller<TSegmenter>::RunSegmentation()
// Time monitoring // Time monitoring
ShowTime(t); ShowTime(t);
std::cout << "accumulatedMemory=" << accumulatedMemory << std::endl;
while(accumulatedMemory > m_Memory && isFusion) while(accumulatedMemory > m_Memory && isFusion)
{ {
...@@ -91,7 +105,7 @@ void Controller<TSegmenter>::RunSegmentation() ...@@ -91,7 +105,7 @@ void Controller<TSegmenter>::RunSegmentation()
isFusion); isFusion);
// Update the given number of iterations // Update the given number of iterations
numberOfIterations -= numberOfIterationsForPartialSegmentations; numberOfIterationsRemaining -= numberOfIterationsForPartialSegmentations;
// Gathering useful variables // Gathering useful variables
GatherUsefulVariables(accumulatedMemory, isFusion); GatherUsefulVariables(accumulatedMemory, isFusion);
...@@ -119,7 +133,7 @@ void Controller<TSegmenter>::RunSegmentation() ...@@ -119,7 +133,7 @@ void Controller<TSegmenter>::RunSegmentation()
m_InputImage->GetLargestPossibleRegion().GetSize()[0], m_InputImage->GetLargestPossibleRegion().GetSize()[0],
m_InputImage->GetLargestPossibleRegion().GetSize()[1], m_InputImage->GetLargestPossibleRegion().GetSize()[1],
m_InputImage->GetNumberOfComponentsPerPixel(), m_InputImage->GetNumberOfComponentsPerPixel(),
numberOfIterations); numberOfIterationsRemaining);
ShowTime(t); ShowTime(t);
...@@ -144,102 +158,158 @@ void Controller<TSegmenter>::RunSegmentation() ...@@ -144,102 +158,158 @@ void Controller<TSegmenter>::RunSegmentation()
template<class TSegmenter> template<class TSegmenter>
long unsigned int Controller<TSegmenter>::GetMaximumNumberOfNodesInMemory() long unsigned int Controller<TSegmenter>::GetMaximumNumberOfNodesInMemory()
{ {
itkDebugMacro(<< "Computing maximum number of nodes in memory");
m_Memory = getMemorySize(); m_Memory = getMemorySize();
assert(m_Memory > 0); assert(m_Memory > 0);
m_Memory /= 2; // For safety and can prevent out of memory troubles m_Memory /= 2; // For safety and can prevent out of memory troubles
// Compute the size of an initial segment TSegmenter segmenter;
using NodeType = typename TSegmenter::NodeType; segmenter.SetInput(m_InputImage);
using NodePointer = typename TSegmenter::NodePointerType;
using EdgeType = typename TSegmenter::EdgeType; unsigned int nodeMemory = segmenter.GetNodeMemory(4); // 4 neighborhood
return std::ceil(((float) m_Memory) / ((float) nodeMemory));
}
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.
unsigned int tileDimension = std::min(width, height);
niter = 1;
unsigned int maxMargin = tileDimension/2;
margin = static_cast<unsigned int>(pow(2, niter + 1) - 2);
unsigned int prevMargin = margin;
// long long unsigned int sizePerNode = sizeof(NodePointer) + sizeof(NodeType) + 1 + 4 *(sizeof(EdgeType) + sizeof(float)); // last term is specific to BS. while(margin < maxMargin)
long long unsigned int sizePerNode = sizeof(NodePointer); {
sizePerNode += sizeof(NodeType) + 1; prevMargin = margin;
sizePerNode += sizeof(float) * m_InputImage->GetNumberOfComponentsPerPixel(); //Specific to BS: means, squeredMeans, spectralSums, stds niter++;
sizePerNode += 4 * (sizeof(EdgeType) + sizeof(float)); // 4 neighbors margin = static_cast<unsigned int>(pow(2, niter + 1) - 2);
long unsigned int maximumNumberOfNodes = (long unsigned int) std::ceil(((float) m_Memory) / ((float) sizePerNode)); }
return maximumNumberOfNodes; niter--;
itkDebugMacro(<< "Number of iterations=" << niter << " margin=" << margin);
} }
/* /*
* Compute a tiling layout such as tiles respect following constraints, * Compute a tiling layout which minimizes a criterion based on tile compacity
* sorted by priority: * and memory usage
* 1.number of nodes in a regular tile should fit the available memory
* 2.regular tiles compactness must be the smallest
*/ */
template<class TSegmenter> template<class TSegmenter>
void Controller<TSegmenter>::GetAutomaticConfiguration() void Controller<TSegmenter>::GetAutomaticConfiguration()
{ {
itkDebugMacro(<<"Get automatic configuration");
// Compute the maximum number of nodes that can fit the memory // Compute the maximum number of nodes that can fit the memory
unsigned long int maximumNumberOfNodesInMemory = GetMaximumNumberOfNodesInMemory(); unsigned long int maximumNumberOfNodesInMemory = GetMaximumNumberOfNodesInMemory();
itkDebugMacro(<<"Maximum number of nodes in memory is " << maximumNumberOfNodesInMemory);
// With a margin, this number will be actually 4 times greater // Number of nodes in the entire image
// (assuming that maxMargin = tileDimension/2)
maximumNumberOfNodesInMemory /= 4;
// Find the largest tile size
const unsigned int imageWidth = m_InputImage->GetLargestPossibleRegion().GetSize()[0]; const unsigned int imageWidth = m_InputImage->GetLargestPossibleRegion().GetSize()[0];
const unsigned int imageHeight = m_InputImage->GetLargestPossibleRegion().GetSize()[1]; const unsigned int imageHeight = m_InputImage->GetLargestPossibleRegion().GetSize()[1];
unsigned int nbOfTiles = 1; // total number of tiles const unsigned long int nbOfNodesInImage = imageWidth*imageHeight;
// Initialize layout with 1x1
unsigned int nbOfTiles = 1; // Number of tiles in the layout
m_NbTilesX = 1; m_NbTilesX = 1;
m_NbTilesY = 1; m_NbTilesY = 1;
const unsigned long int nbOfNodesInImage = imageWidth*imageHeight;
unsigned int tileWidth(0), tileHeight(0);
while( nbOfNodesInImage / ((float) nbOfTiles) > maximumNumberOfNodesInMemory)
{
nbOfTiles++;
}
nbOfTiles--;
// Get the multiples of k. For each one, compute the // Without margins, the number of tiles maximizing memory use
// layout which has the minimum tile compactness // is equal to: nbOfNodesInImage / maximumNumberOfNodesInMemory.
float lowestCompactness = itk::NumericTraits<float>::max(); // Actually, there is tile margins. And the best scenario is to have
for (unsigned int layoutNCol = 1; layoutNCol<=nbOfTiles; layoutNCol++) // 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++)
{ {
if (nbOfTiles % layoutNCol == 0) // Is it a multiple of the nb of Tiles? // Get the multiples of k. For each one, compute the criterion of the tiling
for (unsigned int layoutNCol = 1; layoutNCol<=nbOfTiles; layoutNCol++)
{ {
// Compute tile compactness #ifdef OTB_USE_MPI
unsigned int layoutNRow = nbOfTiles / layoutNCol; // We want number of tiles which is a multiple of the number of MPI processes
tileWidth = imageWidth / layoutNCol; if (nbOfTiles % layoutNCol == 0 && // Is it a multiple of the nb of ...Tiles?
tileHeight = imageHeight / layoutNRow; nbOfTiles % otb::MPIConfig::Instance()->GetNbProcs() == 0) // ...nProcs?
float perimeter = tileWidth + tileHeight; #else
float surface = tileWidth * tileHeight; if (nbOfTiles % layoutNCol == 0) // Is it a multiple of the nb of Tiles?
float compactness = perimeter / surface; #endif
{
// Update minimum compactness // Tiling layout
if (lowestCompactness > compactness) unsigned int layoutNRow = nbOfTiles / layoutNCol;
{ unsigned int tileWidth = imageWidth / layoutNCol;
lowestCompactness = compactness; unsigned int tileHeight = imageHeight / layoutNRow;
m_NbTilesX = layoutNCol;
m_NbTilesY = layoutNRow; // Compute margin for regular tiles of this layout
} unsigned int maxMargin, maxIter;
} ComputeMaximumStabilityMargin(tileWidth, tileHeight, maxIter, maxMargin);
} // for each multiple of k 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) vcl_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 // Compute the tile size
m_TileWidth = static_cast<unsigned int>(imageWidth/m_NbTilesX); m_TileWidth = static_cast<unsigned int>(imageWidth/m_NbTilesX);
m_TileHeight = static_cast<unsigned int>(imageHeight/m_NbTilesY); m_TileHeight = static_cast<unsigned int>(imageHeight/m_NbTilesY);
itkDebugMacro(<<"Selected layout: " << m_NbTilesX << "x" << m_NbTilesY
// Compute the stability margin. The naive strategy consider a margin value and a stable size equal. << " (criterion=" << lowestCriterionValue << ")");
unsigned int tileDimension = std::min(m_TileWidth, m_TileHeight);
unsigned int niter = 1; // Compute the stability margin
unsigned int maxMargin = tileDimension/2; ComputeMaximumStabilityMargin(m_TileWidth, m_TileHeight,m_NumberOfFirstIterations, m_Margin);
unsigned int currMargin = static_cast<unsigned int>(pow(2, niter + 1) - 2);
unsigned int prevMargin = currMargin; TSegmenter segmenter;
segmenter.SetInput(m_InputImage);
while(currMargin < maxMargin) unsigned int nodeMemory = segmenter.GetNodeMemory(4); // 4 neighborhood
{ unsigned int memoryUsed = (m_TileHeight + 2*m_Margin)*(m_TileWidth + 2*m_Margin)*nodeMemory;
prevMargin = currMargin; itkWarningMacro(<< "An amount of " << (memoryUsed / (1024.0*1024.0)) << " Mbytes of RAM will be used for regular tiles of size "
niter++; << (m_TileWidth + 2*m_Margin) << "x" << (m_TileHeight + 2*m_Margin) );
currMargin = static_cast<unsigned int>(pow(2, niter + 1) - 2);
}
m_Margin = prevMargin;
m_NumberOfFirstIterations = niter - 1;
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment