Commit d8ebd795 authored by Pierre Lassalle's avatar Pierre Lassalle

Final streaming version of the grm, good luck Rémi ;)

parent 7f113d8b
#=========================================================================
# Program: Large Scale Generic Region Merging Library (LSGRM)
# Language: C++
# author: Lassalle Pierre
# Copyright (c) Centre National d'Etudes Spatiales. All rights reserved
# See grmlib-copyright.txt for details.
# This software is distributed WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the above copyright notices for more information.
#=========================================================================
add_executable(Test lsgrmProto.cxx)
target_link_libraries(Test OTBLSGRM)
#include <iostream>
#include "lsrmBaatzSegmenter.h"
#include "lsgrmController.h"
int main(int argc, char *argv[])
{
if(argc != 8)
{
// img/test.tif tiles/ 125 125 4 tmp/
std::cerr << "[input image] [tile directory] [tile width] [tile height] [number of first iterations] [temporary directory] [output directory]"
<< std::endl;
return 1;
}
/* Parse command line arguments */
const std::string imagePath = argv[1];
std::string tileDir = argv[2]; // May be corrected if badly written.
const unsigned int tileWidth = atoi(argv[3]);
const unsigned int tileHeight = atoi(argv[4]);
const unsigned int niter = atoi(argv[5]);
std::string tmpDir = argv[6]; // May be corrected if badly written.
std::string outDir = argv[7];
/*
To add:
internal memory available
If we have to do the image division
if we have to clean up the directory
the output directory in case the global graph cannot fit in memory
*/
using ImageType = otb::VectorImage<float, 2>;
using SegmenterType = lsrm::BaatzSegmenter<ImageType>;
using ControllerType = lsgrm::Controller<SegmenterType>;
ControllerType controller;
controller.SetInputImage(imagePath);
controller.SetTileDirectory(tileDir);
controller.SetTemporaryDirectory(tmpDir);
controller.SetOutputGraphDirectory(outDir);
// Memory configuration
controller.SetInternalMemoryAvailable(512ul);
controller.SetTileWidth(500);
controller.SetTileHeight(500);
controller.SetNumberOfFirstIterations(6);
// Specific parameters
lsrm::BaatzParam params;
params.m_SpectralWeight = 0.7;
params.m_ShapeWeight = 0.3;
controller.SetSpecificParameters(params);
controller.SetThreshold(60*60);
controller.RunSegmentation();
return 0;
}
#=========================================================================
# Program: Large Scale Generic Region Merging Library (GRM)
# Language: C++
# author: Lassalle Pierre
# Copyright (c) Centre National d'Etudes Spatiales. All rights reserved
# See grmlib-copyright.txt for details.
# This software is distributed WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the above copyright notices for more information.
#=========================================================================
project(LSGRM)
cmake_minimum_required(VERSION 2.8)
find_package(OTB)
IF(OTB_FOUND)
include(${OTB_USE_FILE})
ELSE(OTB_FOUND)
message(FATAL_ERROR
"Cannot build OTB project without OTB. Please set OTB_DIR.")
ENDIF(OTB_FOUND)
#Activate c++11
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
if(COMPILER_SUPPORTS_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fpermissive -Wall -Wmaybe-uninitialized")
else()
message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/Code)
add_subdirectory(Code)
add_subdirectory(Applications)
#=========================================================================
# Program: Large Scale Generic Region Merging Library (GRM)
# Language: C++
# author: Lassalle Pierre
# Copyright (c) Centre National d'Etudes Spatiales. All rights reserved
# See grmlib-copyright.txt for details.
# This software is distributed WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the above copyright notices for more information.
#=========================================================================add_library(OTBGRM
add_library(OTBLSGRM
lsrmNeighborhood.cpp
lpContour.cpp)
target_link_libraries(OTBLSGRM ${OTB_LIBRARIES})
#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
#endif
#ifndef __LSGRM_CONTROLLER_H
#define __LSGRM_CONTROLLER_H
#include "lsrmGetInternalMemory.h"
#include "lsgrmSplitter.h"
#include "lsgrmGraphOperations.h"
namespace lsgrm
{
template<class TSegmenter>
class Controller
{
public:
/* Some convenient typedefs */
using SegmenterType = TSegmenter;
using ImageType = typename SegmenterType::ImageType;
using SegmentationParameterType = typename SegmenterType::ParameterType;
/* Default constructor and destructor. */
Controller();
~Controller();
void RunSegmentation();
void SetImageDivision(bool f);
void SetInputImage(const std::string& str);
void SetTileDirectory(const std::string& str);
void SetTemporaryDirectory(const std::string& str);
void SetOutputGraphDirectory(const std::string& str);
void SetTileWidth(const unsigned int v);
void SetTileHeight(const unsigned int v);
void SetNumberOfFirstIterations(const unsigned int v);
void SetSpecificParameters(const SegmentationParameterType& params);
void SetThreshold(const float& t);
void SetInternalMemoryAvailable(long long unsigned int v); // expecting a value in Mbytes.
private:
void GetAutomaticConfiguration();
void RetrieveProblemConfiguration();
/* Parameters given by the user */
long long unsigned int m_Memory; // RAM available for the computation.
std::string m_InputImage; // Input image path (if the image needs to be divided).
std::string m_TileDirectory; // Directory containing tiles (if the process of dividing is not activated
// then the directory must contain the tiles and the info.txt file.)
std::string m_TemporaryDirectory; // Directory used to store intermediate files during the process.
std::string m_OutputGraphDirectory;
std::string m_OutputLabelImage; // Output label image path.
bool m_ImageDivisionActivated; // The input image must be divided.
bool m_CleanTemporaryDirectory; // Clean the temporary directory.
/* Specific segmentation parameters */
SegmentationParameterType m_SpecificParameters;
float m_Threshold;
/* Internal attribute members.*/
unsigned int m_ImageWidth;
unsigned int m_ImageHeight;
unsigned int m_ImageBands;
unsigned int m_NbTilesX;
unsigned int m_NbTilesY;
unsigned int m_NumberOfFirstIterations;
unsigned int m_Margin;
unsigned int m_TileWidth;
unsigned int m_TileHeight;
std::vector<ProcessingTile> m_Tiles;
};
} // end of namespace lsgrm
#include "lsgrmController.txx"
#endif
This diff is collapsed.
#ifndef __LSGRM_GRAPH_OPERATIONS_H
#define __LSGRM_GRAPH_OPERATIONS_H
#include "lsgrmHeader.h"
#include "lsrmGraphOperations.h"
#include "otbImageFileReader.h"
namespace lsgrm
{
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)
bool margin[4]; // Is there a margin at top, left, bottom or right
};
template<class TSegmenter>
void MergeAllGraphsAndAchieveSegmentation(const typename TSegmenter::ParameterType& params,
const float& threshold,
std::vector<ProcessingTile>& tiles,
const std::string& tmpDir,
const unsigned int nbTilesX,
const unsigned int nbTilesY,
const unsigned int tileWidth,
const unsigned int tileHeight,
const unsigned int imageWidth,
const unsigned int imageHeight,
const unsigned int imageBands,
bool& isFusion,
const std::string& outputGraphDirectory);
template<class TSegmenter>
long long unsigned int RunFirstPartialSegmentation(const typename TSegmenter::ParameterType& params,
const float& threshold,