diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5cc6475 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.ih linguist-language=C++ diff --git a/deformations/.gitignore b/deformations/.gitignore new file mode 100644 index 0000000..4e002af --- /dev/null +++ b/deformations/.gitignore @@ -0,0 +1,2 @@ +build*/ +**/*.swp diff --git a/deformations/3dVolProjector.cpp b/deformations/3dVolProjector.cpp new file mode 100644 index 0000000..ecb5ac9 --- /dev/null +++ b/deformations/3dVolProjector.cpp @@ -0,0 +1,273 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ +/** + * @file visuDistanceTransform.cpp + * @ingroup Examples + * @author Bertrand Kerautret (\c kerautre@loria.fr ) + * LORIA (CNRS, UMR 7503), University of Nancy, France + * + * @date 2011/01/04 + * + * An example file named qglViewer. + * + * This file is part of the DGtal library. + */ + +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include + +// Qt +#include +#include + +#include "DGtal/base/Common.h" +#include "DGtal/io/readers/VolReader.h" +#include "DGtal/io/viewers/Viewer3D.h" +#include "DGtal/io/DrawWithDisplay3DModifier.h" + + +using namespace std; +using namespace DGtal; +using namespace Z3i; + +#include "deformationDisplay3d.h" + +///////////////////// +#include +#include +#include + +namespace po = boost::program_options; + +int displayOneFile( + const string& inputFilename, + const string& outputBasename, + const int& offset = 0, const double& step = 0) +{ + //image reading + typedef ImageSelector::Type Image; + Image image = VolReader::importVol( inputFilename ); + + QCoreApplication* application = QCoreApplication::instance(); + Viewer3D<> viewer; + + //display + // displayPartition(viewer, image); + Domain domain(image.domain()); + + GradientColorMap colorMap( 0, 510 ); + colorMap.addColor(Color::Yellow); + colorMap.addColor(Color::Blue); + colorMap.addColor(Color::Red); + colorMap.addColor(Color::Green); + + for(Domain::ConstIterator it = domain.begin(), itend=domain.end(); it!=itend; ++it){ + unsigned char val= image( *it ); + Color c = colorMap( val ); + if(val > 0){ + viewer << CustomColors3D(c, c); + viewer << *it; + } + } + + viewer.show(); + viewer << Viewer3D<>::updateDisplay; + + // Prepare snapshot and connect it to drawFinished signal + // Ref: http://www.libqglviewer.com/refManual/classQGLViewer.html#a1cf2ffb973b096b249dc7e90327a2a8e + viewer.setSnapshotFileName(outputBasename.c_str()); + viewer.setSnapshotFormat("PNG"); + QObject::connect( &viewer, SIGNAL(drawFinished(bool)), &viewer, SLOT(saveSnapshot(bool)) ); + + // Camera Configuration + if (!viewer.restoreStateFromFile()) + { + string s = viewer.stateFileName().toStdString(); + trace.emphase() << " file " << s + << " not found " + << std::endl; + } + + if ( (offset != 0)&&(step != 0) ) + { + viewer.camera()->setOrientation( -(offset*step), 0.0); + viewer.showEntireScene(); + } + + // Render + viewer.updateGL(); + + + {//rename snapshot + std::stringstream olds; + olds << viewer.snapshotFileName().toStdString() + << "-" << setfill('0') << std::setw(4) + << (viewer.snapshotCounter()-1) << ".png"; + string oldf = olds.str(); + std::stringstream news; + news << outputBasename << ".png"; + string newf = news.str(); + if (rename (oldf.c_str(), newf.c_str()) == -1) + trace.info() << "renaming " << oldf << " into " + << newf << " failed " << std::endl; + } + + /* + {//rename state file + string oldf = viewer.stateFileName().toStdString(); + std::stringstream news; + news << ".qglviewer" << (QGLViewer::QGLViewerIndex(&viewer)+1) << ".xml"; + string newf = news.str(); + if (rename (oldf.c_str(), newf.c_str()) == -1) + trace.info() << "renaming " << oldf << " into " + << newf << " failed " << std::endl; + } + */ + + return 0; +} + +int main( int argc, char** argv ) +{ + // Init Qt with command-line parameters + QApplication application(argc, argv); + + // parse command line ---------------------------------------------- + po::options_description general_opt("Allowed options are: "); + general_opt.add_options() + ("help,h", "display this message") + ("output-file,o", po::value()->default_value("interface"), "output file(s) basename" ) + ("input-file,i", po::value(), "volume file" ) + ("multi-input,mi", po::value(), "volume files basename " ) + ("start,s", po::value()->default_value(1), "starting number (for option -mi)" ) + ("end,e", po::value()->default_value(2), "ending number, not included (for option -mi)" ) + ("angle-step,a", po::value()->default_value(360), "angle step as a fraction of 2PI (0 disables this feature)" ) + ("number,n", po::value()->default_value(90), "number of angle steps when moving camera" ); + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, general_opt), vm); + po::notify(vm); + if(vm.count("help")||argc<=1) + { + std::cout << "Usage: " << argv[0] << " [input-file]\n" + << "Display volume file as a set of digital frontiers" + << general_opt << "\n"; + return 0; + } + + //files + if (!(vm.count("output-file"))) + trace.info() << "output file begin with : interface" << std::endl; + string outputBasename = vm["output-file"].as(); + + + if(vm.count("input-file")) + { + if (vm.count("multi-input")) + { + trace.error() << " Cannot use both input options in the same time " + << endl; + return 1; + } + string inputFilename = vm["input-file"].as(); + + if(vm.count("angle-step")) + { + int den = vm["angle-step"].as(); + double angleStep = (den == 0)?0.0:( (2.0*M_PI)/((double) den) ); + int n = vm["number"].as(); + for (int i = 0; i < n; ++i) + { + trace.info() << i << std::endl; + std::stringstream so; + so << outputBasename << setfill('0') << std::setw(4) + << i; + + displayOneFile(inputFilename, so.str(), i+1, angleStep); + } + + {//rename state file + std::stringstream olds; + olds << ".qglviewer" << n << ".xml"; + string oldf = olds.str(); + string newf = ".qglviewer.xml"; + if (rename (oldf.c_str(), newf.c_str()) == -1) + trace.info() << "renaming " << oldf << " into " + << newf << " failed " << std::endl; + } + + } + else + { + displayOneFile(inputFilename, outputBasename); + + {//rename state file + string oldf = ".qglviewer1.xml"; + string newf = ".qglviewer.xml"; + if (rename (oldf.c_str(), newf.c_str()) == -1) + trace.info() << "renaming " << oldf << " into " + << newf << " failed " << std::endl; + } + } + } + else + { + if (vm.count("multi-input")) + { + string inputBasename = vm["multi-input"].as(); + + int den = vm["angle-step"].as(); + double angleStep = (den == 0)?0.0:( (2.0*M_PI)/((double) den) ); + + int start = vm["start"].as(); + int end = vm["end"].as(); + for (int i = start; i != end; ++i) + { + std::stringstream si; + si << inputBasename << setfill('0') << std::setw(4) + << i << ".vol"; + trace.info() << si.str() << std::endl; + std::stringstream so; + so << outputBasename << setfill('0') << std::setw(4) + << i; + + displayOneFile(si.str(), so.str(), i+1, angleStep); + + } + + + {//rename state file + std::stringstream olds; + olds << ".qglviewer" << (end-start) << ".xml"; + string oldf = olds.str(); + string newf = ".qglviewer.xml"; + if (rename (oldf.c_str(), newf.c_str()) == -1) + trace.info() << "renaming " << oldf << " into " + << newf << " failed " << std::endl; + } + + } + else + { + trace.error() << " No file name defined" << endl; + return 1; + } + } + + + return 0; +} diff --git a/deformations/3dVolViewer.cpp b/deformations/3dVolViewer.cpp index c4434d8..a78e5e0 100644 --- a/deformations/3dVolViewer.cpp +++ b/deformations/3dVolViewer.cpp @@ -31,6 +31,7 @@ #include #include "DGtal/base/Common.h" +#include "DGtal/helpers/StdDefs.h" #include "DGtal/io/readers/VolReader.h" #include "DGtal/io/viewers/Viewer3D.h" #include "DGtal/io/DrawWithDisplay3DModifier.h" @@ -53,6 +54,9 @@ namespace po = boost::program_options; int main( int argc, char** argv ) { + // Qt init with command-line parameters + QApplication application(argc, argv); + // parse command line ---------------------------------------------- po::options_description general_opt("Allowed options are: "); general_opt.add_options() @@ -82,8 +86,7 @@ int main( int argc, char** argv ) int thresholdMax = vm["thresholdMax"].as(); unsigned char transp = vm["transparency"].as(); - QApplication application(argc,argv); - Viewer3D viewer; + Viewer3D<> viewer; viewer.setWindowTitle("simple Volume Viewer"); viewer.show(); @@ -108,6 +111,6 @@ int main( int argc, char** argv ) viewer << *it; } } - viewer << Viewer3D::updateDisplay; + viewer << Viewer3D<>::updateDisplay; return application.exec(); } diff --git a/deformations/BinaryPredicates.h b/deformations/BinaryPredicates.h new file mode 100644 index 0000000..b4fe43b --- /dev/null +++ b/deformations/BinaryPredicates.h @@ -0,0 +1,91 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +#pragma once + +/** + * @file BinaryPredicates.h + * + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * @date 2012/02/02 + * + * This files contains several basic classes representing binary predicates + * + * This file is part of the DGtal library. + */ + +#if defined(BinaryPredicates_RECURSES) +#error Recursive header files inclusion detected in BinaryPredicates.h +#else // defined(BinaryPredicates_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define BinaryPredicates_RECURSES + +#if !defined BinaryPredicates_h +/** Prevents repeated inclusion of headers. */ +#define BinaryPredicates_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include "DGtal/base/Common.h" +////////////////////////////////////////////////////////////////////////////// + +namespace DGtal +{ + + ///////////////////////////////////////////////////////////////////////////// + // template class ConstantBinaryPredicate + /** + * Description of template class 'ConstantPointPredicate'

+ * \brief Aim: The predicate that returns always the same value boolCst + * + * @tparam boolCst any boolean value + */ + template + struct ConstantBinaryPredicate + { + /** + * @param t1 first argument + * @param t2 second argument + * @tparam T1 type of the first argument + * @tparam T2 type of the second argument + * @return the value of the predicate + */ + template + bool operator()( const T1& t1, const T2& t2 ) const + { + return boolCst; + } + + }; // end of class ConstantBinaryPredicate + + typedef ConstantBinaryPredicate TrueBinaryPredicate; + typedef ConstantBinaryPredicate FalseBinaryPredicate; + +} // namespace DGtal + + +/////////////////////////////////////////////////////////////////////////////// +// Includes inline functions. +//#include "BinaryPredicates.ih" + +// // +/////////////////////////////////////////////////////////////////////////////// + +#endif // !defined BinaryPredicates_h + +#undef BinaryPredicates_RECURSES +#endif // else defined(BinaryPredicates_RECURSES) diff --git a/deformations/CMakeLists.txt b/deformations/CMakeLists.txt index 8c9fd0b..f5605d0 100644 --- a/deformations/CMakeLists.txt +++ b/deformations/CMakeLists.txt @@ -1,5 +1,7 @@ PROJECT(Deformations) +#C++11 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") #DGtal CMAKE_MINIMUM_REQUIRED(VERSION 2.6) @@ -12,10 +14,15 @@ LINK_DIRECTORIES(${DGTAL_LIBRARY_DIRS}) #Inclusion de Boost include(FindBoost) -find_package(Boost 1.36.0 REQUIRED program_options) -message(STATUS "Found Boost: ${Boost_LIBRARIES} ") +find_package(Boost 1.49.0 COMPONENTS program_options) +message(STATUS "Found Boost: ${Boost_LIBRARIES} in ${Boost_LIBRARY_DIRS} and ${Boost_INCLUDE_DIRS} ") +message(STATUS "More precisely: ${Boost_PROGRAM_OPTIONS_LIBRARY} ") link_directories(${Boost_LIBRARY_DIRS}) include_directories(${Boost_INCLUDE_DIRS}) +link_directories(${Boost_LIBRARY_DIRS}) + +# Workaround for strange bug while trying to include boost_program_options +SET(BOOST_LIBRARIES ${BOOST_LIBRARIES} boost_program_options) #fftw INCLUDE_DIRECTORIES(/usr/include/) @@ -25,6 +32,18 @@ LINK_DIRECTORIES(/usr/lib/) ADD_EXECUTABLE(3dVolViewer 3dVolViewer) TARGET_LINK_LIBRARIES(3dVolViewer ${DGTAL_LIBRARIES} ${BOOST_LIBRARIES}) +ADD_EXECUTABLE(3dVolProjector 3dVolProjector) +TARGET_LINK_LIBRARIES(3dVolProjector ${DGTAL_LIBRARIES} ${BOOST_LIBRARIES}) + +ADD_EXECUTABLE(generate3d generate3d) +TARGET_LINK_LIBRARIES(generate3d ${DGTAL_LIBRARIES} ${BOOST_LIBRARIES}) + +#ADD_EXECUTABLE(testLocalDeformation3d testLocalDeformation3d) +#TARGET_LINK_LIBRARIES(testLocalDeformation3d ${DGTAL_LIBRARIES} ${BOOST_LIBRARIES}) + +#ADD_EXECUTABLE(testLocalDeformation2d testLocalDeformation2d) +#TARGET_LINK_LIBRARIES(testLocalDeformation2d ${DGTAL_LIBRARIES} ${BOOST_LIBRARIES}) + ADD_EXECUTABLE(testDisk testDisk) TARGET_LINK_LIBRARIES(testDisk ${DGTAL_LIBRARIES} ${BOOST_LIBRARIES} fftw3) @@ -35,4 +54,5 @@ ADD_EXECUTABLE(deformation3d deformation3d) TARGET_LINK_LIBRARIES(deformation3d ${DGTAL_LIBRARIES} ${BOOST_LIBRARIES} fftw3) ADD_EXECUTABLE(imageBlurring imageBlurring) -TARGET_LINK_LIBRARIES(imageBlurring ${DGTAL_LIBRARIES} fftw3) +TARGET_LINK_LIBRARIES(imageBlurring ${DGTAL_LIBRARIES} ${BOOST_LIBRARIES} fftw3) + diff --git a/deformations/DifferentialOperators.h b/deformations/DifferentialOperators.h new file mode 100644 index 0000000..77d3bfa --- /dev/null +++ b/deformations/DifferentialOperators.h @@ -0,0 +1,999 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +#pragma once + +/** + * @file DifferentialOperators.h + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * + * @date 201/12/19 + * + * Header file for module DifferentialOperators.cpp + * + * This file is part of the DGtal library. + */ + +#if defined(DifferentialOperators_RECURSES) +#error Recursive header files inclusion detected in DifferentialOperators.h +#else // defined(DifferentialOperators_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define DifferentialOperators_RECURSES + +#if !defined DifferentialOperators_h +/** Prevents repeated inclusion of headers. */ +#define DifferentialOperators_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include +#include "DGtal/images/CConstImage.h" + +////////////////////////////////////////////////////////////////////////////// + +namespace DGtal +{ + + + ///////////////////////////////////////////////////////////////////////////// + // template class ForwardDifference + /** + * Description of template class 'ForwardDifference'

+ * \brief Aim: Computes the forward difference at a point. + * + * + * @tparam TFonctor model of CPointFunctor + * @tparam TPointPredicate model of CPointPredicate + * @tparam TOutputValue type of returned value (default TFunctor::Value) + */ + template + class ForwardDifference + { + + BOOST_CONCEPT_ASSERT(( concepts::CConstImage )); + + // ----------------------- Types ------------------------------ + public: + + typedef TImage Image; + typedef TOutputValue OutputValue; + typedef typename Image::Value Value; + + typedef typename Image::Point Point; + typedef typename Image::Vector Vector; + typedef typename Image::Domain Domain; + + typedef typename Image::Dimension Dimension; + static const typename Image::Dimension dimension = Image::dimension; + + // ----------------------- Standard services ------------------------------ + public: + + + /** + * Constructor. + * + * @param aStartingImage any image of signed values + * @param aGridStep any length (=1 by default) + */ + ForwardDifference( Image& aStartingImage, const OutputValue& aGridStep = 1); + + /** + * Destructor. Does nothing. + */ + ~ForwardDifference() {} + + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const {return true;} + + /** + * Difference. + * + * @param aPoint the point where the derivative is computed + * @param aDim the axis along which the derivative is computed + * @return first derivative along axis @a aDim at @ aPoint + */ + OutputValue operator() ( const Point& aPoint, const Dimension& aDim ) const; + + // ------------------------- Private Datas -------------------------------- + private: + + /** + * Reference on an image + */ + Image& myU; + + /** + * Grid step + */ + OutputValue myH; + + }; + + ///////////////////////////////////////////////////////////////////////////// + // template class BackwardDifference + /** + * Description of template class 'BackwardDifference'

+ * \brief Aim: Computes the backward difference at a point + * of an image. + * + * @code + * @endcode + * + * @tparam TImage type of image + * @tparam TOutputValue type of returned value (default TImage::Value) + */ + template + class BackwardDifference + { + + BOOST_CONCEPT_ASSERT(( concepts::CConstImage )); + + // ----------------------- Types ------------------------------ + public: + + typedef TImage Image; + typedef TOutputValue OutputValue; + typedef typename Image::Value Value; + + typedef typename Image::Point Point; + typedef typename Image::Vector Vector; + typedef typename Image::Domain Domain; + + typedef typename Image::Dimension Dimension; + static const typename Image::Dimension dimension = Image::dimension; + + // ----------------------- Standard services ------------------------------ + public: + + + /** + * Constructor. + * + * @param aStartingImage any image of signed values + * @param aGridStep any length (=1 by default) + */ + BackwardDifference( Image& aStartingImage, const OutputValue& aGridStep = 1); + + /** + * Destructor. Does nothing. + */ + ~BackwardDifference() {} + + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const {return true;} + + /** + * Difference. + * + * @param aPoint the point where the derivative is computed + * @param aDim the axis along which the derivative is computed + * @return first derivative along axis @a aDim at @ aPoint + */ + OutputValue operator() ( const Point& aPoint, const Dimension& aDim ) const; + + // ------------------------- Private Datas -------------------------------- + private: + + /** + * Reference on an image + */ + Image& myU; + + /** + * Grid step + */ + OutputValue myH; + + }; + + ///////////////////////////////////////////////////////////////////////////// + // template class CentralDifference + /** + * Description of template class 'CentralDifference'

+ * \brief Aim: Computes the backward difference at a point + * of an image. + * + * @code + * @endcode + * + * @tparam TImage type of image + * @tparam TOutputValue type of returned value (default TImage::Value) + */ + template + class CentralDifference + { + + BOOST_CONCEPT_ASSERT(( concepts::CConstImage )); + + // ----------------------- Types ------------------------------ + public: + + typedef TImage Image; + typedef TOutputValue OutputValue; + typedef typename Image::Value Value; + + typedef typename Image::Point Point; + typedef typename Image::Vector Vector; + typedef typename Image::Domain Domain; + + typedef typename Image::Dimension Dimension; + static const typename Image::Dimension dimension = Image::dimension; + + // ----------------------- Standard services ------------------------------ + public: + + + /** + * Constructor. + * + * @param aStartingImage any image of signed values + * @param aGridStep any length (=1 by default) + */ + CentralDifference( Image& aStartingImage, const OutputValue& aGridStep = 1); + + /** + * Destructor. Does nothing. + */ + ~CentralDifference() {} + + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const {return true;} + + /** + * Difference. + * + * @param aPoint the point where the derivative is computed + * @param aDim the axis along which the derivative is computed + * @return first derivative along axis @a aDim at @ aPoint + */ + OutputValue operator() ( const Point& aPoint, const Dimension& aDim ) const; + + // ------------------------- Private Datas -------------------------------- + private: + + /** + * Reference on an image + */ + Image& myU; + + /** + * Grid step + */ + OutputValue myH; + + }; + + + ///////////////////////////////////////////////////////////////////////////// + // template class Difference2 + /** + * Description of template class 'Difference2'

+ * \brief Aim: Computes the second difference at a point + * of an image. + * + * @code + * @endcode + * + * @tparam TImage type of image + * @tparam TOutputValue type of returned value (default TImage::Value) + */ + template + class Difference2 + { + + BOOST_CONCEPT_ASSERT(( concepts::CConstImage )); + + // ----------------------- Types ------------------------------ + public: + + typedef TImage Image; + typedef TOutputValue OutputValue; + typedef typename Image::Value Value; + + typedef typename Image::Point Point; + typedef typename Image::Vector Vector; + typedef typename Image::Domain Domain; + + typedef typename Image::Dimension Dimension; + static const typename Image::Dimension dimension = Image::dimension; + + // ----------------------- Standard services ------------------------------ + public: + + + /** + * Constructor. + * + * @param aStartingImage any image of signed values + * @param aGridStep any length (=1 by default) + */ + Difference2( Image& aStartingImage, const OutputValue& aGridStep = 1); + + /** + * Destructor. Does nothing. + */ + ~Difference2() {} + + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const {return true;} + + /** + * Second forward/backward difference. + * + * @param aPoint the point where the derivative is computed + * @param aDim the axis along which the derivative is computed + * @return first derivative along axis @a aDim at @ aPoint + */ + OutputValue operator() ( const Point& aPoint, const Dimension& aDim ) const; + + // ------------------------- Private Datas -------------------------------- + private: + + /** + * Reference on an image + */ + Image& myU; + + /** + * Grid step + */ + OutputValue myH; + + }; + + ///////////////////////////////////////////////////////////////////////////// + // template class NormalizedDifference2 + /** + * Description of template class 'NormalizedDifference2'

+ * \brief Aim: Computes the second difference at a point + * of an image. + * + * @code + * @endcode + * + * @tparam TImage type of image + * @tparam TOutputValue type of returned value (default TImage::Value) + */ + template + class NormalizedDifference2 + { + + BOOST_CONCEPT_ASSERT(( concepts::CConstImage )); + + // ----------------------- Types ------------------------------ + public: + + typedef TImage Image; + typedef TOutputValue OutputValue; + typedef typename Image::Value Value; + + typedef typename Image::Point Point; + typedef typename Image::Vector Vector; + typedef typename Image::Domain Domain; + + typedef typename Image::Dimension Dimension; + static const typename Image::Dimension dimension = Image::dimension; + + // ----------------------- Standard services ------------------------------ + public: + + + /** + * Constructor. + * + * @param aStartingImage any image of signed values + * @param aGridStep any length (=1 by default) + */ + NormalizedDifference2( Image& aStartingImage, const OutputValue& aGridStep = 1); + + /** + * Destructor. Does nothing. + */ + ~NormalizedDifference2() {} + + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const {return true;} + + /** + * Second forward/backward difference + * normalized by the inverse of the gradient modulus + * + * @param aPoint the point where the derivative is computed + * @param aDim the axis along which the derivative is computed + * @return first derivative along axis @a aDim at @ aPoint + */ + OutputValue operator() ( const Point& aPoint, const Dimension& aDim ) const; + + // ------------------------- Private Datas -------------------------------- + private: + + /** + * Reference on an image + */ + Image& myU; + + /** + * Grid step + */ + OutputValue myH; + + // ------------------------- Internals -------------------------------- + private: + + /** + * Return the harmonic average of the inverse of @a aV1 and @a aV2 + * + * @param aV1 a first value + * @param aV2 a second value + * @return the average + */ + double average ( const Value& aV1, const Value& aN2 ) const; + + }; + + ///////////////////////////////////////////////////////////////////////////// + // template class WeightedDifference2 + /** + * Description of template class 'WeightedDifference2'

+ * \brief Aim: Computes the second difference at a point + * of an image. + * + * @code + * @endcode + * + * @tparam TImage type of image + * @tparam TOutputValue type of returned value (default TImage::Value) + */ + template + class WeightedDifference2 + { + + BOOST_CONCEPT_ASSERT(( concepts::CConstImage )); + + // ----------------------- Types ------------------------------ + public: + + typedef TImage Image; + typedef TOutputValue OutputValue; + typedef typename Image::Value Value; + + typedef typename Image::Point Point; + typedef typename Image::Vector Vector; + typedef typename Image::Domain Domain; + + typedef typename Image::Dimension Dimension; + static const typename Image::Dimension dimension = Image::dimension; + + // ----------------------- Standard services ------------------------------ + public: + + + /** + * Constructor. + * + * @param aImage any image + * @param aWImage any image of weights + * @param aGridStep any length (=1 by default) + */ + WeightedDifference2( Image& aImage, Image& aWImage, const OutputValue& aGridStep = 1); + + /** + * Destructor. Does nothing. + */ + ~WeightedDifference2() {} + + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const {return true;} + + /** + * Second forward/backward differences on @a aImage + * weighted by @a aWImage and normalized + * by the inverse of the gradient modulus + * + * @param aPoint the point where the derivative is computed + * @param aDim the axis along which the derivative is computed + * @return second derivative of @a aImage along axis @a aDim at @ aPoint + */ + OutputValue operator() ( const Point& aPoint, const Dimension& aDim ) const; + + // ------------------------- Private Datas -------------------------------- + private: + + /** + * Reference on an image + */ + Image& myImage; + /** + * Reference on the weights image + */ + Image& myWImage; + + /** + * Grid step + */ + OutputValue myH; + + // ------------------------- Internals -------------------------------- + private: + + /** + * Return the harmonic average of @a aV1 and @a aV2, + * @a aV1 and @a aV2 being normalized by @a aN1 and @a aN2. + * + * @param aV1 a first value + * @param aN1 any value dividing @a aV1 + * @param aV2 a second value + * @param aN2 any value dividing @a aV2 + * @return the normalized harmonic average of @a aV1 and @a aV2 + */ + double average ( const Value& aV1, const double& aN1, + const Value& aV2, const double& aN2 ) const; + + }; + + /////////////////////////////////////////////////////////////////// + + ///////////////////////////////////////////////////////////////////////////// + // template class Gradient + /** + * Description of template class 'Gradient'

+ * \brief Aim: Computes the gradient at a point + * of an image. + * + * @code + * @endcode + * + * @tparam TDifference type of directionnal differential operator + */ + template + class Gradient + { + + + // ----------------------- Types ------------------------------ + public: + + typedef TDifference FiniteDifference; + typedef typename FiniteDifference::Dimension Dimension; + static const typename FiniteDifference::Dimension dimension = FiniteDifference::dimension; + typedef typename FiniteDifference::Image Image; + typedef typename FiniteDifference::Point Point; + + typedef DGtal::PointVector OutputValue; + + // ----------------------- Standard services ------------------------------ + public: + + /** + * Constructor. + * + * @param aD a finite difference operator + */ + Gradient( const FiniteDifference& aD): myD(aD) {} + + + /** + * Constructor. + * + * @param aStartingImage any image of signed values + * @param aGridStep any length (=1 by default) + */ + Gradient( typename FiniteDifference::Image& aStartingImage, + const typename FiniteDifference::OutputValue& aGridStep = 1); + + /** + * Destructor. Does nothing. + */ + ~Gradient() {} + + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const {return true;} + + /** + * Gradient + * + * @param aPoint the point where the gradient is computed + * @return gradient of @a myU at @ aPoint + */ + OutputValue operator() ( const Point& aPoint ) const; + + // ------------------------- Private Datas -------------------------------- + private: + + /** + * Finite difference + */ + FiniteDifference myD; + + }; + + ///////////////////////////////////////////////////////////////////////////// + // template class UpwindGradient + /** + * Description of template class 'UpwindGradient'

+ * \brief Aim: Computes the gradient at a point + * of an image. + * + * @code + * @endcode + * + * @tparam TDifference type of directionnal differential operator + */ + template + class UpwindGradient + { + + + // ----------------------- Types ------------------------------ + public: + + BOOST_CONCEPT_ASSERT(( concepts::CConstImage )); + + // ----------------------- Types ------------------------------ + public: + + typedef TImage Image; + typedef typename Image::Value Value; + + typedef typename Image::Point Point; + typedef typename Image::Vector Vector; + typedef typename Image::Domain Domain; + + typedef typename Image::Dimension Dimension; + static const typename Image::Dimension dimension = Image::dimension; + + typedef DGtal::PointVector OutputValue; + + // ----------------------- Standard services ------------------------------ + public: + + /** + * Constructor. + * + * @param aStartingImage any image of signed values + * @param aGridStep any length (=1 by default) + */ + UpwindGradient( Image& aStartingImage, + const TOutputValue& aGridStep = 1); + + /** + * Destructor. Does nothing. + */ + ~UpwindGradient() {} + + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const {return true;} + + /** + * Gradient + * + * @param aPoint the point where the gradient is computed + * @param aVector displacement vector of the interface + * @return gradient of @a myU at @ aPoint + */ + OutputValue operator() ( const Point& aPoint, const Vector& aVector ) const; + + // ------------------------- Private Datas -------------------------------- + private: + + /** + * Forward difference + */ + ForwardDifference myF; + /** + * Backward difference + */ + BackwardDifference myB; + + }; + + ///////////////////////////////////////////////////////////////////////////// + // template class GodunovGradient + /** + * Description of template class 'GodunovGradient'

+ * \brief Aim: Computes the gradient at a point + * of an image. + * + * @code + * @endcode + * + * @tparam TDifference type of directionnal differential operator + */ + template + class GodunovGradient + { + + + // ----------------------- Types ------------------------------ + public: + + BOOST_CONCEPT_ASSERT(( concepts::CConstImage )); + + // ----------------------- Types ------------------------------ + public: + + typedef TImage Image; + typedef typename Image::Value Value; + + typedef typename Image::Point Point; + typedef typename Image::Vector Vector; + typedef typename Image::Domain Domain; + + typedef typename Image::Dimension Dimension; + static const typename Image::Dimension dimension = Image::dimension; + + typedef DGtal::PointVector OutputValue; + + // ----------------------- Standard services ------------------------------ + public: + + /** + * Constructor. + * + * @param aStartingImage any image + * @param isPositive flag equal to 'true' if the + * interface moves in the direction of the normal + * and 'false' if it moves in the opposite direction + * (='true' by default) + * @param aGridStep any length (=1 by default) + */ + GodunovGradient( Image& aStartingImage, bool isPositive = true, + const TOutputValue& aGridStep = 1); + + /** + * Destructor. Does nothing. + */ + ~GodunovGradient() {} + + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const {return true;} + + /** + * Gradient + * + * @param aPoint the point where the gradient is computed + * @return gradient of @a myU at @ aPoint + */ + OutputValue operator() ( const Point& aPoint ) const; + + /** + * Gradient + * + * @param aPoint the point where the gradient is computed + * @param isPositive flag equal to 'true' if the + * interface moves in the direction of the normal + * and 'false' if it moves in the opposite direction + * (='true' by default) + * @return gradient of @a myU at @ aPoint + */ + OutputValue operator() ( const Point& aPoint, bool isPositive ) const; + + // ------------------------- Private Datas -------------------------------- + private: + + /** + * Forward difference + */ + ForwardDifference myF; + /** + * Backward difference + */ + BackwardDifference myB; + /** + * Flag + */ + bool myIsPositive; + }; + + ///////////////////////////////////////////////////////////////////////////// + // template class Divergence + /** + * Description of template class 'Divergence'

+ * \brief Aim: Computes the divergence at a point + * of an image. + * + * @code + * @endcode + * + * @tparam TDifference type of directionnal differential operator + */ + template + class Divergence + { + + + // ----------------------- Types ------------------------------ + public: + + typedef TDifference FiniteDifference; + typedef typename FiniteDifference::Dimension Dimension; + static const typename FiniteDifference::Dimension dimension = FiniteDifference::dimension; + typedef typename FiniteDifference::Image Image; + typedef typename FiniteDifference::Point Point; + typedef typename FiniteDifference::OutputValue OutputValue; + + // ----------------------- Standard services ------------------------------ + public: + + /** + * Constructor. + * + * @param aD a finite difference operator + */ + Divergence( const FiniteDifference& aD ): myD(aD) {} + + /** + * Constructor. + * + * @param aStartingImage any image of signed values + * @param aGridStep any length (=1 by default) + */ + Divergence( typename FiniteDifference::Image& aStartingImage, + const typename FiniteDifference::OutputValue& aGridStep = 1); + + /** + * Destructor. Does nothing. + */ + ~Divergence() {} + + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const {return true;} + + /** + * Divergence + * + * @param aPoint the point where the divergence is computed + * @return gradient of @a myU at @ aPoint + */ + OutputValue operator() ( const Point& aPoint ) const; + + // ------------------------- Private Datas -------------------------------- + private: + + /** + * Finite difference + */ + FiniteDifference myD; + + }; + + ///////////////////////////////////////////////////////////////////////////// + // template class GradientModulus + /** + * Description of template class 'GradientModulus'

+ * \brief Aim: Computes the gradient modulus at a point + * of an image. + * + * @code + * @endcode + * + * @tparam TGradient type of gradient + */ + template + class GradientModulus + { + + + // ----------------------- Types ------------------------------ + public: + + typedef TGradient Gradient; + typedef typename Gradient::Dimension Dimension; + static const typename Gradient::Dimension dimension = Gradient::dimension; + typedef typename Gradient::Point Point; + + typedef double OutputValue; + + // ----------------------- Standard services ------------------------------ + public: + + /** + * Constructor. + * + * @param aG any gradient operator + */ + GradientModulus( const Gradient& aG) : myG(aG) {} + + /** + * Constructor. + * + * @param aStartingImage any image of signed values + * @param aGridStep any length (=1 by default) + */ + GradientModulus( typename Gradient::Image& aStartingImage, + const typename Gradient::OutputValue::Component& aGridStep = 1); + + + /** + * Destructor. Does nothing. + */ + ~GradientModulus() {} + + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const {return true;} + + /** + * Gradient modulus + * + * @param aPoint the point where the gradient modulus is computed + * @return gradient modulus of @a myU at @ aPoint + */ + OutputValue operator() ( const Point& aPoint ) const; + + // ------------------------- Private Datas -------------------------------- + private: + + /** + * Gradient + */ + Gradient myG; + + }; + + + /////////////////////////////////////////////////////////////////// + + + +} // namespace DGtal + + +/////////////////////////////////////////////////////////////////////////////// +// Includes inline functions. +#include "DifferentialOperators.ih" + +// // +/////////////////////////////////////////////////////////////////////////////// + +#endif // !defined DifferentialOperators_h + +#undef DifferentialOperators_RECURSES +#endif // else defined(DifferentialOperators_RECURSES) diff --git a/deformations/DifferentialOperators.ih b/deformations/DifferentialOperators.ih new file mode 100644 index 0000000..a8132b5 --- /dev/null +++ b/deformations/DifferentialOperators.ih @@ -0,0 +1,470 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +/** + * @file DifferentialOperators.ih + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * + * @date 2011/12/19 + * + * @brief Implementation of inline methods defined in DifferentialOperators.h + * + * This file is part of the DGtal library. + */ + + +////////////////////////////////////////////////////////////////////////////// +#include +#include +////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// IMPLEMENTATION of inline methods. +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// --------------------One-dimensional differential operators ----------------- + +template +inline +DGtal::ForwardDifference +::ForwardDifference( Image& aStartingImage, const OutputValue& aGridStep ) + : myU( aStartingImage ), myH( aGridStep ) +{ +} + +template +inline +typename DGtal::ForwardDifference::OutputValue +DGtal::ForwardDifference:: +operator() (const Point& aPoint, const Dimension& aDim) const +{ + ASSERT( (aDim >= 0) && (aDim < dimension) ); + + Point next = aPoint; + next[aDim] += 1; + if ( myU.domain().isInside(next) ) + return ( (myU(next) - myU(aPoint)) / myH ); + else + { + Point prev = aPoint; + prev[aDim] -= 1; + if ( myU.domain().isInside(prev) ) { + return ( (myU(aPoint) - myU(prev)) / myH ); + } else { + ASSERT( false && "too small image" ) ; + throw std::domain_error("too small image"); + } + } +} + +template +inline +DGtal::BackwardDifference +::BackwardDifference( Image& aStartingImage, const OutputValue& aGridStep ) + : myU( aStartingImage ), myH( aGridStep ) +{ +} + +template +inline +typename DGtal::BackwardDifference::OutputValue +DGtal::BackwardDifference:: +operator() (const Point& aPoint, const Dimension& aDim) const +{ + ASSERT( (aDim >= 0) && (aDim < dimension) ); + + Point prev = aPoint; + prev[aDim] -= 1; + + if ( myU.domain().isInside(prev) ) + return ( (myU(aPoint) - myU(prev)) / myH ); + else + { + Point next = aPoint; + next[aDim] += 1; + if ( myU.domain().isInside(next) ) { + return ( (myU(next) - myU(aPoint)) / myH ); + } else { + ASSERT( false && "too small image" ) ; + throw std::domain_error("too small image"); + } + } +} + +template +inline +DGtal::CentralDifference +::CentralDifference( Image& aStartingImage, const OutputValue& aGridStep ) + : myU( aStartingImage ), myH( aGridStep ) +{ +} + +template +inline +typename DGtal::CentralDifference::OutputValue +DGtal::CentralDifference:: +operator() (const Point& aPoint, const Dimension& aDim) const +{ + ASSERT( (aDim >= 0) && (aDim < dimension) ); + + Domain d = myU.domain(); + Point next = aPoint; + next[aDim] += 1; + bool nextInside = d.isInside(next); + Point prev = aPoint; + prev[aDim] -= 1; + bool prevInside = d.isInside(prev); + + if (nextInside && prevInside ) + return ( (myU(next) - myU(prev)) / (2*myH) ); + else if ( nextInside && (!prevInside) ) + return ( (myU(next) - myU(aPoint)) / myH ); + else if ( (!nextInside) && prevInside ) + return ( (myU(aPoint) - myU(prev)) / myH ); + else { + ASSERT( false && "too small image" ) ; + throw std::domain_error("too small image"); + } +} + +template +inline +DGtal::Difference2 +::Difference2( Image& aStartingImage, const OutputValue& aGridStep ) + : myU( aStartingImage ), myH( aGridStep ) +{ +} + +template +inline +typename DGtal::Difference2::OutputValue +DGtal::Difference2:: +operator() (const Point& aPoint, const Dimension& aDim) const +{ + ASSERT( (aDim >= 0) && (aDim < dimension) ); + + Domain d = myU.domain(); + Point next = aPoint; + next[aDim] += 1; + bool nextInside = d.isInside(next); + Point previous = aPoint; + previous[aDim] -= 1; + bool previousInside = d.isInside(previous); + + if (nextInside && previousInside ) + return ( (myU(next) - 2*myU(aPoint) + myU(previous) ) / (myH*myH) ); + else + return 0; +} + +template +inline +DGtal::NormalizedDifference2 +::NormalizedDifference2( Image& aStartingImage, const OutputValue& aGridStep ) + : myU( aStartingImage ), myH( aGridStep ) +{ +} + +template +inline +typename DGtal::NormalizedDifference2::OutputValue +DGtal::NormalizedDifference2:: +operator() (const Point& aPoint, const Dimension& aDim) const +{ + ASSERT( (aDim >= 0) && (aDim < dimension) ); + + Domain d = myU.domain(); + Point next = aPoint; + next[aDim] += 1; + bool nextInside = d.isInside(next); + Point previous = aPoint; + previous[aDim] -= 1; + bool previousInside = d.isInside(previous); + + typedef Gradient > Gradient; + GradientModulus m( myU ); + double mod = m(aPoint); + + if (nextInside && previousInside ) + { + double nMod = m(next); + double pMod = m(previous); + return ( ( average(nMod,mod)*(myU(next) - myU(aPoint)) ) + - ( average(mod,pMod)*(myU(aPoint) - myU(previous)) ) + / NumberTraits::castToDouble(myH*myH) ); + } + else if ( (!nextInside) && previousInside ) + { + double pMod = m(previous); + return ( ( ( 1 - average(pMod,mod) ) + *(myU(aPoint) - myU(previous)) ) + / NumberTraits::castToDouble(myH*myH) ); + } + else if ( (!previousInside) && nextInside ) + { + double nMod = m(next); + return ( ( ( average(nMod,mod) - 1 ) + *(myU(next) - myU(aPoint)) ) + / NumberTraits::castToDouble(myH*myH) ); + } + else { + ASSERT( false && "too small image" ) ; + throw std::domain_error("too small image"); + } +} + +template +inline +double +DGtal::NormalizedDifference2:: +average( const Value& aV1, const Value& aV2 ) const +{ + double som = NumberTraits::castToDouble(aV1 + aV2); + if (som == 0) return 0; + else return 2/som; +} + +template +inline +DGtal::WeightedDifference2 +::WeightedDifference2( Image& aImage, Image& aWImage, const OutputValue& aGridStep ) + : myImage( aImage ), myWImage( aWImage), myH( aGridStep ) +{ + ASSERT(aWImage.domain().lowerBound() == aImage.domain().lowerBound()); + ASSERT(aWImage.domain().upperBound() == aImage.domain().upperBound()); +} + +template +inline +typename DGtal::WeightedDifference2::OutputValue +DGtal::WeightedDifference2:: +operator() (const Point& aPoint, const Dimension& aDim) const +{ + ASSERT( (aDim >= 0) && (aDim < dimension) ); + + Domain d = myImage.domain(); + Point next = aPoint; + next[aDim] += 1; + bool nextInside = d.isInside(next); + Point previous = aPoint; + previous[aDim] -= 1; + bool previousInside = d.isInside(previous); + + typedef Gradient > Gradient; + GradientModulus m( myImage ); + double mod = m(aPoint); + + if (nextInside && previousInside ) + { + double nMod = m(next); + double pMod = m(previous); + return ( ( average(myWImage(next),nMod,myWImage(aPoint),mod) + *(myImage(next) - myImage(aPoint)) ) + - ( average(myWImage(aPoint),mod,myWImage(previous),pMod) + *(myImage(aPoint) - myImage(previous)) ) + / NumberTraits::castToDouble(myH*myH) ); + } + else if ( (!nextInside) && previousInside ) + { + double pMod = m(previous); + return ( ( ( myWImage(aPoint) + - average(myWImage(previous),pMod,myWImage(aPoint),mod) ) + *(myImage(aPoint) - myImage(previous)) ) + / NumberTraits::castToDouble(myH*myH) ); + } + else if ( (!previousInside) && nextInside ) + { + double nMod = m(next); + return ( ( ( average(myWImage(next),nMod,myWImage(aPoint),mod) + - myWImage(aPoint) ) + *(myImage(next) - myImage(aPoint)) ) + / NumberTraits::castToDouble(myH*myH) ); + } + else { + ASSERT( false && "too small image" ) ; + throw std::domain_error("too small image"); + } + +} + +template +inline +double +DGtal::WeightedDifference2:: +average( const Value& aV1, const double& aN1, + const Value& aV2, const double& aN2 ) const +{ + ASSERT(aV1 != 0); + ASSERT(aV2 != 0); + double q1 = aN1 / NumberTraits::castToDouble(aV1); + double q2 = aN2 / NumberTraits::castToDouble(aV2); + double sum = q1 + q2; + if (sum == 0) return 0; + else return 2/sum; +} + +/////////////////////////////////////////////////////////////////////////////// +// -------------------- Gradient operators ----------------- + + +template +inline +DGtal::Gradient +::Gradient( typename FiniteDifference::Image& aStartingImage, + const typename FiniteDifference::OutputValue& aGridStep ) + : myD( FiniteDifference( aStartingImage, aGridStep ) ) +{ +} + +template +inline +typename DGtal::Gradient::OutputValue +DGtal::Gradient:: +operator() (const Point& aPoint) const +{ + OutputValue g; + for (Dimension k = 0; k < dimension; ++k ) + { + g[k] = myD(aPoint,k); + } + return g; +} + + +template +inline +DGtal::UpwindGradient +::UpwindGradient( Image& aStartingImage, const TOutputValue& aGridStep ) + : myF( ForwardDifference( aStartingImage, aGridStep ) ), + myB( BackwardDifference( aStartingImage, aGridStep ) ) +{ +} + +template +inline +typename DGtal::UpwindGradient::OutputValue +DGtal::UpwindGradient:: + operator() (const Point& aPoint, const Vector& aVector) const +{ + OutputValue g; + for (Dimension k = 0; k < dimension; ++k ) + { + if (aVector[k] > 0) + g[k] = myB(aPoint,k); + else if (aVector[k] < 0) + g[k] = myF(aPoint,k); + else //== 0 + g[k] = 0; + } + return g; +} + +template +inline +DGtal::GodunovGradient +::GodunovGradient( Image& aStartingImage, bool isPositive, const TOutputValue& aGridStep ) + : myF( ForwardDifference( aStartingImage, aGridStep ) ), + myB( BackwardDifference( aStartingImage, aGridStep ) ), + myIsPositive( isPositive ) +{ +} + +template +inline +typename DGtal::GodunovGradient::OutputValue +DGtal::GodunovGradient:: + operator() (const Point& aPoint) const +{ + return operator() (aPoint,myIsPositive); +} + +template +inline +typename DGtal::GodunovGradient::OutputValue +DGtal::GodunovGradient:: +operator() (const Point& aPoint, bool isPositive) const +{ + OutputValue g; + for (Dimension k = 0; k < dimension; ++k ) + { + if (isPositive) + { + typename OutputValue::Coordinate a = myB(aPoint,k); + if (a < 0) a = 0; + typename OutputValue::Coordinate b = myF(aPoint,k); + if (b > 0) b = 0; + g[k] = std::sqrt( std::max(a*a,b*b) ); + } + else + { + typename OutputValue::Coordinate a = myB(aPoint,k); + if (a > 0) a = 0; + typename OutputValue::Coordinate b = myF(aPoint,k); + if (b < 0) b = 0; + g[k] = std::sqrt( std::max(a*a,b*b) ); + } + } + return g; +} + + +template +inline +DGtal::GradientModulus +::GradientModulus( typename Gradient::Image& aStartingImage, + const typename Gradient::OutputValue::Component& aGridStep ) + : myG( Gradient( aStartingImage, aGridStep ) ) +{ +} + +template +inline +typename DGtal::GradientModulus::OutputValue +DGtal::GradientModulus:: +operator() (const Point& aPoint) const +{ + typename Gradient::OutputValue g = myG( aPoint ); + return g.norm( Gradient::OutputValue::L_2 ); +} + +/////////////////////////////////////////////////////////////////////////////// +// ----------------------- Divergence Operator ------------------------------ + + +template +inline +DGtal::Divergence +::Divergence( typename FiniteDifference::Image& aStartingImage, + const typename FiniteDifference::OutputValue& aGridStep ) + : myD( FiniteDifference( aStartingImage, aGridStep ) ) +{ +} + +template +inline +typename DGtal::Divergence::OutputValue +DGtal::Divergence:: +operator() (const Point& aPoint) const +{ + OutputValue sum = 0; + for (Dimension k = 0; k < dimension; ++k ) + { + sum += myD(aPoint,k); + } + return sum; +} diff --git a/deformations/ExactDiffusionEvolver.h b/deformations/ExactDiffusionEvolver.h index 804afa4..72f5cad 100644 --- a/deformations/ExactDiffusionEvolver.h +++ b/deformations/ExactDiffusionEvolver.h @@ -70,7 +70,7 @@ namespace DGtal { //ASSERT - BOOST_CONCEPT_ASSERT(( CImage )); + BOOST_CONCEPT_ASSERT(( concepts::CImage )); // ----------------------- Types ------------------------------ public: diff --git a/deformations/ExactDiffusionEvolver.ih b/deformations/ExactDiffusionEvolver.ih index a42aece..13cdf20 100644 --- a/deformations/ExactDiffusionEvolver.ih +++ b/deformations/ExactDiffusionEvolver.ih @@ -81,7 +81,7 @@ DGtal::ExactDiffusionEvolver::update(Image& aF, const double& aT) for (unsigned int k = 0; k < Point::dimension; ++k) { //normalization and centering (to be in [-0.5,0.5[^d) - double coord = (double) p.at(k)/v.at(k); + double coord = (double) p[k]/v[k]; if (coord >= 0.5) coord -= 1.0; norm2 += coord*coord; } diff --git a/deformations/ExactReactionEvolver.h b/deformations/ExactReactionEvolver.h index b3ac392..306c05e 100644 --- a/deformations/ExactReactionEvolver.h +++ b/deformations/ExactReactionEvolver.h @@ -65,7 +65,7 @@ namespace DGtal { //ASSERT - BOOST_CONCEPT_ASSERT(( CImage )); + BOOST_CONCEPT_ASSERT(( concepts::CImage )); // ----------------------- Types ------------------------------ public: diff --git a/deformations/ExplicitReactionEvolver.h b/deformations/ExplicitReactionEvolver.h index 1542b08..7521261 100644 --- a/deformations/ExplicitReactionEvolver.h +++ b/deformations/ExplicitReactionEvolver.h @@ -45,7 +45,7 @@ #include "DGtal/base/Common.h" #include "DGtal/images/CImage.h" -#include "DGtal/images/DifferentialOperators.h" +#include "DifferentialOperators.h" ////////////////////////////////////////////////////////////////////////////// @@ -68,7 +68,7 @@ namespace DGtal { //ASSERT - BOOST_CONCEPT_ASSERT(( CImage )); + BOOST_CONCEPT_ASSERT(( concepts::CImage )); // ----------------------- Types ------------------------------ public: diff --git a/deformations/ExplicitReactionEvolver.ih b/deformations/ExplicitReactionEvolver.ih index 33ea367..b8690e9 100644 --- a/deformations/ExplicitReactionEvolver.ih +++ b/deformations/ExplicitReactionEvolver.ih @@ -63,6 +63,9 @@ DGtal::ExplicitReactionEvolver::update(Image& aF, const double { ASSERT(aT >= 0); + // Result of the update + Image next_aF(aF.domain()); + double k = 0; if (myWithVolumeConservation) k = force(aF); @@ -80,12 +83,13 @@ DGtal::ExplicitReactionEvolver::update(Image& aF, const double GodunovGradient gradient( aF, (myG >= 0) ); GradientModulus > m( gradient ); //new value - double tmp = derivative (v) - - myEpsilon * ( myG + f*m(*it) + k) * std::sqrt( 2*function(v) ); + double tmp = derivative (v) - myEpsilon * ( myG + f*m(*it) + k) * std::sqrt( 2*function(v) ); Value newValue = v - ( aT / (myEpsilon * myEpsilon ) ) * tmp; - aF.setValue( *it, static_cast(newValue) ); + next_aF.setValue( *it, static_cast(newValue) ); } + // Update + aF = next_aF; } @@ -167,4 +171,6 @@ DGtal::operator<< ( std::ostream & out, // // /////////////////////////////////////////////////////////////////////////////// +/* GNU coding style */ +/* vim: set ts=2 sw=2 expandtab cindent cinoptions=>4,n-2,{2,^-2,:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1 : */ diff --git a/deformations/FFT.h b/deformations/FFT.h index c31794a..9e547bf 100644 --- a/deformations/FFT.h +++ b/deformations/FFT.h @@ -6,6 +6,7 @@ #include #include +#include namespace DGtal{ @@ -20,7 +21,7 @@ namespace DGtal{ { //ASSERT - BOOST_CONCEPT_ASSERT(( CImage )); + BOOST_CONCEPT_ASSERT(( concepts::CImage )); // ----------------------- Types ------------------------------ public: diff --git a/deformations/FFT.ih b/deformations/FFT.ih index 4a13d55..f16aad2 100644 --- a/deformations/FFT.ih +++ b/deformations/FFT.ih @@ -29,22 +29,21 @@ DGtal::FFT::compute(ComplexImage& anImage) //image size Vector v = myImage.extent(); - int t[dimension]; + int t[dimension]; int n = 1; - for (unsigned int i= 0; i < dimension; ++i) - { - t[i] = v.at(i); - n *= t[i]; + for (int i = 0; i < dimension; ++i) + { //row-major order + t[dimension-1-i] = v[i]; + n *= v[i]; } -//using fftw library + //using fftw library //input and output arrays fftw_complex* spatial_repr; fftw_complex* frequency_repr; spatial_repr=(fftw_complex*) fftw_malloc(sizeof(fftw_complex)*n); frequency_repr=(fftw_complex*) fftw_malloc(sizeof(fftw_complex)*n); - //fill the arrays Domain d = myImage.domain(); typename Domain::ConstIterator it = d.begin(); typename Domain::ConstIterator itEnd = d.end(); diff --git a/deformations/FMM.h b/deformations/FMM.h new file mode 100644 index 0000000..0c92582 --- /dev/null +++ b/deformations/FMM.h @@ -0,0 +1,498 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +#pragma once + +/** + * @file FMM.h + * + * @author Tristan Roussillon (\c + * tristan.roussillon@liris.cnrs.fr ) Laboratoire d'InfoRmatique en + * Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, + * France + * + * @date 2012/01/17 + * + * @brief Fast Marching Method for incremental distance transform + * + * This file is part of the DGtal library. + * + */ + +#if defined(FMM_RECURSES) +#error Recursive header files inclusion detected in FMM.h +#else // defined(FMM_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define FMM_RECURSES + +#if !defined FMM_h +/** Prevents repeated inclusion of headers. */ +#define FMM_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include +#include +#include +#include +#include "DGtal/base/Common.h" +#include "DGtal/images/CImage.h" +#include "DGtal/images/ImageHelper.h" +#include "DGtal/kernel/sets/CDigitalSet.h" +#include "DGtal/kernel/sets/SetPredicate.h" +#include "DGtal/kernel/CPointPredicate.h" +#include "DGtal/kernel/CPointFunctor.h" + +#include "FMMPointFunctors.h" + +////////////////////////////////////////////////////////////////////////////// + +namespace DGtal +{ + + namespace details + { + template + class PointValueCompare { + public: + bool operator()(const T& a, const T& b) + { + if ( std::abs(a.second) == std::abs(b.second) ) + { //point comparison + return (a.first < b.first); + } + else //distance comparison + //(in absolute value in order to deal with + //signed distance values) + return ( std::abs(a.second) < std::abs(b.second) ); + } + }; + } + + ///////////////////////////////////////////////////////////////////////////// + // template class FMM + /** + * Description of template class 'FMM'

+ * \brief Aim: Fast Marching Method (FMM) for nd distance transforms. + * + * In this approach, a signed distance function is computing at + * each digital point by marching out from an initial set of points, + * for which the values of the signed distance are known. This set + * is an initialization of the so-called @e accepted @e point @e set. + * Each digital point adjacent to one of the accepted points is + * put into the so-called @e candidate @e point @e set. + * A tentative value is computed for its signed distance, using + * only the values of the accepted points lying in its neighborhood. + * This task is delegated to an instance of a point functor, + * which is defined as L2FirstOrderLocalDistance by default. + * However, you are free to use L2SecondOrderLocalDistance, which provides + * more accurate distance values, L1FirstOrderLocalDistance and + * LInfFirstOrderLocalDistance for other norms. + * + * Then the point of smallest tentative value is added to the set of + * accepted points. The tentative values of the candidates adjacent + * to the newly added point are updated using the the distance value + * of the newly added point. The search of the point of smallest + * tentative value is accelerated using a STL set of pairs (point, + * tentative value). + * + * @tparam TImage any model of CImage + * @tparam TSet any model of CDigitalSet + * @tparam TPointPredicate any model of CPointPredicate, + * used to bound the computation within a domain + * @tparam TPointFunctor any model of CPointFunctor, + * used to compute the new distance value + * + * You can define the FMM type as follows: + @snippet geometry/volumes/distance/exampleFMM3D.cpp FMMDef + * + * You can run the algorithm as follows (d is a domain): + @snippet geometry/volumes/distance/exampleFMM3D.cpp FMMUsage + * + * @see exampleFMM2D.cpp + * @see exampleFMM3D.cpp + * @see testFMM.cpp + */ + template > + class FMM + { + + // ----------------------- Types ------------------------------ + public: + + + //concept assert + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + BOOST_CONCEPT_ASSERT(( concepts::CDigitalSet )); + BOOST_CONCEPT_ASSERT(( concepts::CPointPredicate )); + BOOST_CONCEPT_ASSERT(( concepts::CPointFunctor )); + + typedef TImage Image; + typedef TSet AcceptedPointSet; + typedef TPointPredicate PointPredicate; + + //points + typedef typename Image::Point Vector; + typedef typename Image::Point Point; + BOOST_STATIC_ASSERT(( boost::is_same< Point, typename AcceptedPointSet::Point >::value )); + BOOST_STATIC_ASSERT(( boost::is_same< Point, typename PointPredicate::Point >::value )); + + //dimension + typedef typename Point::Dimension Dimension; + static const Dimension dimension = Point::dimension; + + //distance + typedef TPointFunctor PointFunctor; + typedef typename PointFunctor::Value Value; + + + private: + + //intern data types + typedef std::pair PointValue; + typedef std::set > CandidatePointSet; + typedef unsigned long Area; + + // ------------------------- Private Datas -------------------------------- + private: + + /** + * Reference on the image + */ + Image& myImage; + + /** + * Reference on the set of accepted points + */ + AcceptedPointSet& myAcceptedPoints; + + /** + * Set of candidate points + */ + CandidatePointSet myCandidatePoints; + + /** + * Point functor used to deduce + * the distance of a new point + * from the distance of its neighbors + */ + PointFunctor myPF; + + /** + * Constant reference on a point predicate that returns + * 'true' inside the domain + * where the distance transform is performed + */ + const PointPredicate& myPointPredicate; + + /** + * Area threshold (in number of accepted points) + * above which the propagation stops + */ + Area myAreaThreshold; + + /** + * Value threshold above which the propagation stops + */ + Value myValueThreshold; + + /** + * Min value + */ + Value myMinValue; + + /** + * Max value + */ + Value myMaxValue; + + + // ----------------------- Standard services ------------------------------ + public: + + /** + * Constructor. + * + * @see init + */ + FMM(Image& aImg, AcceptedPointSet& aSet, + const PointPredicate& aPointPredicate); + + /** + * Constructor. + * + * @see init + */ + FMM(Image& aImg, AcceptedPointSet& aSet, + const PointPredicate& aPointPredicate, + const Area& aAreaThreshold, const Value& aValueThreshold); + + /** + * Constructor. + * + * @see init + */ + FMM(Image& aImg, AcceptedPointSet& aSet, + const PointPredicate& aPointPredicate, + const PointFunctor& aPointFunctor ); + + /** + * Constructor. + * + * @see init + */ + FMM(Image& aImg, AcceptedPointSet& aSet, + const PointPredicate& aPointPredicate, + const Area& aAreaThreshold, const Value& aValueThreshold, + const PointFunctor& aPointFunctor ); + + /** + * Destructor. + */ + ~FMM(); + + + // ----------------------- Interface -------------------------------------- + public: + + /** + * Computes the distance map + */ + void compute(); + + /** + * Insert the candidate of min distance into the set + * of accepted points if it is possible and then + * update the set of candidate points. + * + * @param aPoint inserted point (if inserted) + * @param aValue its distance value (if inserted) + * + * @return 'true' if the point of min distance is accepted + * 'false' otherwise. + * + * @see addNewAcceptedPoint + */ + bool computeOneStep(Point& aPoint, Value& aValue); + + /** + * Minimal distance value in the set of accepted points. + * + * @return minimal distance value. + */ + Value min() const; + + /** + * Maximal distance value in the set of accepted points. + * + */ + Value max() const; + + /** + * Computes the minimal distance value in the set of accepted points. + * + * NB: in O(n log n) where n is the size of the set + * + * @return minimal distance value. + */ + Value getMin() const; + + /** + * Computes the maximal distance value in the set of accepted points. + * + * NB: in O(n log n) where n is the size of the set + * + * @return maximal distance value. + */ + Value getMax() const; + + /** + * Writes/Displays the object on an output stream. + * @param out the output stream where the object is written. + */ + void selfDisplay ( std::ostream & out ) const; + + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const; + + // ------------------------- static functions for init -------------------- + + + /** + * Initialize @a aImg and @a aSet from the points of the range [@a itb , @a ite ) + * Assign a distance equal to @a aValue + * + * @param itb begin iterator (on points) + * @param ite end iterator (on points) + * @param aImg the distance image + * @param aSet the set of points for which the distance has been assigned + * @param aValue distance default value + */ + template + static void initFromPointsRange(const TIteratorOnPoints& itb, const TIteratorOnPoints& ite, + Image& aImg, AcceptedPointSet& aSet, + const Value& aValue); + + /** + * Initialize @a aImg and @a aSet from the points + * incident to the signed cells of the range [@a itb , @a ite ) + * Assign to the inner points a distance equal to - @a aValue + * if @a aFlagIsPositive is 'false' (default) but @a aValue otherwise, + * and conversely for the outer points. + * + * @param itb begin iterator (on signed cells) + * @param ite end iterator (on signed cells) + * @param aImg the distance image + * @param aSet the set of points for which the distance has been assigned + * @param aValue distance default value + */ + template + static void initFromBelsRange(const KSpace& aK, + const TIteratorOnBels& itb, const TIteratorOnBels& ite, + Image& aImg, AcceptedPointSet& aSet, + const Value& aValue, + bool aFlagIsPositive = false); + /** + * Initialize @a aImg and @a aSet from the points + * incident to the signed cells of the range [@a itb , @a ite ) + * Assign to the inner points a distance equal to - @a aValue + * if @a aFlagIsPositive is 'false' (default) but @a aValue otherwise, + * and conversely for the outer points. + * + * @param itb begin iterator (on signed cells) + * @param ite end iterator (on signed cells) + * @param aF any implicit function + * @param aImg the distance image + * @param aSet the set of points for which the distance has been assigned + */ + template + static void initFromBelsRange(const KSpace& aK, + const TIteratorOnBels& itb, const TIteratorOnBels& ite, + const TImplicitFunction& aF, + Image& aImg, AcceptedPointSet& aSet, + bool aFlagIsPositive = false); + + + /** + * Initialize @a aImg and @a aSet from the inner and outer points + * of the range [@a itb , @a ite ) of pairs of points. + * Assign to the inner points a distance equal to - @a aValue + * if @a aFlagIsPositive is 'false' (default) but @a aValue otherwise, + * and conversely for the outer points. + * + * @param itb begin iterator (on points) + * @param ite end iterator (on points) + * @param aImg the distance image + * @param aSet the set of points for which the distance has been assigned + * @param aValue distance default value + */ + template + static void initFromIncidentPointsRange(const TIteratorOnPairs& itb, const TIteratorOnPairs& ite, + Image& aImg, AcceptedPointSet& aSet, + const Value& aValue, + bool aFlagIsPositive = false); + + private: + + /** + * Copy constructor. + * @param other the object to clone. + * Forbidden by default. + */ + FMM ( const FMM & other ); + + /** + * Assignment. + * @param other the object to copy. + * @return a reference on 'this'. + * Forbidden by default. + */ + FMM & operator= ( const FMM & other ); + + // ------------------------- Internals ------------------------------------ + private: + + /** + * Initialize the set of candidate points + */ + void init(); + + /** + * Insert the candidate of min distance into the set + * of accepted points and update the set of candidate points. + * + * @param aPoint inserted point (if true) + * @param aValue distance value of the inserted point (if true) + * + * @return 'true' if the point of min distance is accepted + * 'false' otherwise. + */ + bool addNewAcceptedPoint(Point& aPoint, Value& aValue); + + /** + * Update the set of candidate points with the neigbors of @a aPoint. + * + * @param aPoint any point + */ + void update(const Point& aPoint); + + /** + * Test a new point as a candidate. + * If it is not yet accepted + * and if the point predicate return 'true', + * compute its distance and insert it + * into the set candidate points. + * + * @param aPoint any point + * + * @return 'true' if inserted, + * 'false' otherwise. + */ + bool addNewCandidate(const Point& aPoint); + + + }; // end of class FMM + + + /** + * Overloads 'operator<<' for displaying objects of class 'FMM'. + * @param out the output stream where the object is written. + * @param object the object of class 'FMM' to write. + * @return the output stream after the writing. + */ + template + std::ostream& + operator<< ( std::ostream & out, const FMM & object ); + +} // namespace DGtal + + +/////////////////////////////////////////////////////////////////////////////// +// Includes inline functions. +#include "FMM.ih" + +// // +/////////////////////////////////////////////////////////////////////////////// + +#endif // !defined FMM_h + +#undef FMM_RECURSES +#endif // else defined(FMM_RECURSES) diff --git a/deformations/FMM.ih b/deformations/FMM.ih new file mode 100644 index 0000000..d69f16c --- /dev/null +++ b/deformations/FMM.ih @@ -0,0 +1,509 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +/** + * @file FMM.ih + * @author Tristan Roussillon (\c + * tristan.roussillon@liris.cnrs.fr ) Laboratoire d'InfoRmatique en + * Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, + * France + * + * + * @date 2012/01/17 + * + * @brief Implementation of inline methods defined in FMM.h + * + * This file is part of the DGtal library. + */ + + +////////////////////////////////////////////////////////////////////////////// +#include +////////////////////////////////////////////////////////////////////////////// + +#include "DGtal/topology/SCellsFunctors.h" + +using namespace DGtal::functors; + +/////////////////////////////////////////////////////////////////////////////// +// IMPLEMENTATION of inline methods. +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// ----------------------- Standard services ------------------------------ + +template +inline +DGtal::FMM +::FMM(Image& aImg, AcceptedPointSet& aSet, + const PointPredicate& aPointPredicate) + : myImage( aImg ), myAcceptedPoints( aSet ), myPF( PointFunctor(aImg, aSet) ), + myPointPredicate( aPointPredicate ), + myAreaThreshold( std::numeric_limits::max() ), + myValueThreshold( std::numeric_limits::max() ) +{ + if (myAcceptedPoints.size() == 0) throw InputException(); + init(); +} + + +template +inline +DGtal::FMM +::FMM(Image& aImg, AcceptedPointSet& aSet, + const PointPredicate& aPointPredicate, + const Area& aAreaThreshold, + const Value& aValueThreshold) + : myImage( aImg ), myAcceptedPoints( aSet ), myPF( PointFunctor(aImg, aSet) ), + myPointPredicate( aPointPredicate ), + myAreaThreshold( aAreaThreshold ), + myValueThreshold( aValueThreshold ) +{ + if (myAcceptedPoints.size() == 0) throw InputException(); + init(); +} + + +template +inline +DGtal::FMM +::FMM(Image& aImg, AcceptedPointSet& aSet, + const PointPredicate& aPointPredicate, + const PointFunctor& aPointFunctor) + : myImage( aImg ), myAcceptedPoints( aSet ), myPF( aPointFunctor ), + myPointPredicate( aPointPredicate ), + myAreaThreshold( std::numeric_limits::max() ), + myValueThreshold( std::numeric_limits::max() ) +{ + if (myAcceptedPoints.size() == 0) throw InputException(); + init(); +} + + +template +inline +DGtal::FMM +::FMM(Image& aImg, AcceptedPointSet& aSet, + const PointPredicate& aPointPredicate, + const Area& aAreaThreshold, + const Value& aValueThreshold, + const PointFunctor& aPointFunctor) + : myImage( aImg ), myAcceptedPoints( aSet ), myPF( aPointFunctor ), + myPointPredicate( aPointPredicate ), + myAreaThreshold( aAreaThreshold ), + myValueThreshold( aValueThreshold ) +{ + if (myAcceptedPoints.size() == 0) throw InputException(); + init(); +} + + +template +inline +DGtal::FMM::~FMM() +{ +} + +/////////////////////////////////////////////////////////////////////////////// +// Static functions : + + +template +template +void +DGtal::FMM +::initFromPointsRange(const TIteratorOnPoints& itb, const TIteratorOnPoints& ite, + Image& aImg, AcceptedPointSet& aSet, + const Value& aValue) +{ + + aSet.clear(); + for (TIteratorOnPoints it = itb; it != ite; ++it) + { + insertAndAlwaysSetValue( aImg, aSet, *it, aValue ); + } +} + +template +template +void +DGtal::FMM +::initFromBelsRange(const KSpace& aK, + const TIteratorOnBels& itb, const TIteratorOnBels& ite, + Image& aImg, AcceptedPointSet& aSet, + const Value& aValue, + bool aFlagIsPositive) +{ + Value k = -1; + if (aFlagIsPositive) k = 1; + + aSet.clear(); + for (TIteratorOnBels it = itb; it != ite; ++it) + { + //getting incident points + SCellToIncidentPoints func( aK ); + typename SCellToIncidentPoints::Output points = func( *it ); + //assignement + insertAndAlwaysSetValue( aImg, aSet, points.first, k*aValue ); + insertAndAlwaysSetValue( aImg, aSet, points.second, -k*aValue ); + } +} + +template +template +void +DGtal::FMM +::initFromBelsRange(const KSpace& aK, + const TIteratorOnBels& itb, const TIteratorOnBels& ite, + const TImplicitFunction& aF, + Image& aImg, AcceptedPointSet& aSet, + bool aFlagIsPositive) +{ + Value k = -1; + if (aFlagIsPositive) k = 1; + + /// types + typedef typename KSpace::Cell Bel; + typedef typename TImplicitFunction::Value Value; + typedef std::pair BelValue; + typedef std::map Buffer; + + typedef typename SCellToIncidentPoints::Output Pair; + typedef std::vector IncidentPoints; + SCellToIncidentPoints getIncidentPoints( aK ); + + /// 1) store a value for each bel + /// (== distance of the inner point to the interface) + IncidentPoints incidentPoints; + Buffer buffer; + for (TIteratorOnBels it = itb; it != ite; ++it) + { + //getting incident points + Pair points = getIncidentPoints( *it ); + incidentPoints.push_back( points ); + + //getting values + Value vin = aF( points.first ); + Value vout = aF( points.second ); + + //computing/storing the new value + Value e = std::max(vin, vout) - std::min(vin, vout); + Value v = (std::abs(vin)/e); + // std::cout << points.first << " " << vin << ", " << points.second << " " << vout; + // std::cout << " : " << e << ", " << v << std::endl; + + ASSERT( v >= 0 ); + buffer.insert( BelValue( aK.unsigns( *it ), v ) ); + } + + aSet.clear(); + /// 2) for each inner/outer point + /// computes its distance from the values of its incident bels + typedef L2FirstOrderLocalDistanceFromCells Computer4InnerPts; + typedef L2FirstOrderLocalDistanceFromCells Computer4OuterPts; + Computer4InnerPts computerIn(aK, buffer); + Computer4OuterPts computerOut(aK, buffer); + for (typename IncidentPoints::const_iterator it = incidentPoints.begin(); + it != incidentPoints.end(); ++it) + { + //computing the values + Value vin = computerIn( it->first ); + Value vout = computerOut( it->second ); + // std::cout << points.first << " " << vin << ", " + // << points.second << " " << vout << std::endl; + + //assignement + insertAndAlwaysSetValue( aImg, aSet, it->first, k*vin ); + insertAndAlwaysSetValue( aImg, aSet, it->second, -k*vout ); + } +} + +template +template +void +DGtal::FMM +::initFromIncidentPointsRange(const TIteratorOnPairs& itb, const TIteratorOnPairs& ite, + Image& aImg, AcceptedPointSet& aSet, + const Value& aValue, + bool aFlagIsPositive) +{ + Value k = -1; + if (aFlagIsPositive) k = 1; + + aSet.clear(); + for (TIteratorOnPairs it = itb; it != ite; ++it) + { + insertAndAlwaysSetValue( aImg, aSet, it->first, k*aValue ); + insertAndAlwaysSetValue( aImg, aSet, it->second, -k*aValue ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Interface - public : + + +template +inline +void +DGtal::FMM::compute() +{ + Point p = Point::diagonal(0); + Value d = 0; + while ( addNewAcceptedPoint( p, d ) ) + { } +} + +template +inline +bool +DGtal::FMM +::computeOneStep(Point& aPoint, Value& aValue) +{ + return addNewAcceptedPoint(aPoint, aValue); +} + +template +inline +typename DGtal::FMM::Value +DGtal::FMM::min() const +{ + return myMinValue; +} + +template +inline +typename DGtal::FMM::Value +DGtal::FMM::max() const +{ + return myMaxValue; +} + +template +inline +typename DGtal::FMM::Value +DGtal::FMM::getMin() const +{ + const AcceptedPointSet& set = myAcceptedPoints; + ASSERT( set.size() >= 1 ); + + typename AcceptedPointSet::ConstIterator it = set.begin(); + typename AcceptedPointSet::ConstIterator itEnd = set.end(); + Value vmin = myImage( *it ); + for (++it; it != itEnd; ++it) + { + Value v = myImage( *it ); + if (v < vmin) vmin = v; + } + return vmin; +} + +template +inline +typename DGtal::FMM::Value +DGtal::FMM::getMax() const +{ + const AcceptedPointSet& set = myAcceptedPoints; + ASSERT( set.size() >= 1 ); + + typename AcceptedPointSet::ConstIterator it = set.begin(); + typename AcceptedPointSet::ConstIterator itEnd = set.end(); + Value vmax = myImage( *it ); + for (++it; it != itEnd; ++it) + { + Value v = myImage( *it ); + if (v > vmax) vmax = v; + } + return vmax; +} + +template +inline +bool +DGtal::FMM::isValid() const +{ + //area threshold + if ( (myAcceptedPoints.size() <= 0) + || (myAcceptedPoints.size() >= myAreaThreshold) ) return false; + + //distance threshold + if ( ( getMin() != min() ) || ( getMax() != max() ) ) return false; + if ( (std::abs(getMin()) >= myValueThreshold) + || (getMax() >= myValueThreshold) ) return false; + + //point predicate + bool flagIsOk = true; + const AcceptedPointSet& set = myAcceptedPoints; + typename AcceptedPointSet::ConstIterator it = set.begin(); + typename AcceptedPointSet::ConstIterator itEnd = set.end(); + for ( ; ( (it != itEnd)&&(flagIsOk == true) ); ++it) + { + if (myPointPredicate( *it ) == false) flagIsOk = false; + } + if (!flagIsOk) return false; + + return true; +} + +template +inline +void +DGtal::FMM::selfDisplay ( std::ostream & out ) const +{ + out << "[FMM " << dimension << "d] "; + out << myAcceptedPoints.size() << " accepted points (< " << myAreaThreshold << ")"; + out << " and " << myCandidatePoints.size() << " candidates. "; + myPF.selfDisplay(out); + out << " "; + out << "dmin: " << min() << ", dmax: " << max(); + out << " (abs < " << myValueThreshold << ")" << std::endl; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Internals + +template +inline +void +DGtal::FMM::init() +{ + + myCandidatePoints.clear(); + + typename AcceptedPointSet::Iterator it = myAcceptedPoints.begin(); + typename AcceptedPointSet::Iterator itEnd = myAcceptedPoints.end(); + for ( ; it != itEnd; ++it) + { + update( *it ); + } + + myMinValue = getMin(); + myMaxValue = getMax(); + +} + +template +inline +bool +DGtal::FMM +::addNewAcceptedPoint(Point& aPoint, Value& aValue) +{ + + if ( (myAcceptedPoints.size()+1) < myAreaThreshold ) + {//if a new point can be accepted + + bool flagStop = false; + typename CandidatePointSet::iterator it = myCandidatePoints.begin(); + typename CandidatePointSet::iterator itEnd = myCandidatePoints.end(); + while ( (it != itEnd) && (!flagStop) ) + { //while there are candidates and no point has been accepted + + //pair of min distance + PointValue minPair = *it; + + if ( std::abs(minPair.second) < myValueThreshold ) + { //if distance below a given threshold + + //the point of min distance is removed from the set of candidates + myCandidatePoints.erase(*it); + //it can be inserted into the set of accepted points + if ( insertAndSetValue( myImage, myAcceptedPoints, + minPair.first, minPair.second ) ) + { //if it does not belong to the set + //the set of candidates is updated with + //the neighbors of the new accepted point + aPoint = minPair.first; + aValue = minPair.second; + if (aValue > myMaxValue) myMaxValue = aValue; + if (aValue < myMinValue) myMinValue = aValue; + update( aPoint ); + flagStop = true; + } + else + { //otherwise it has already been accepted + //with a smaller distance and the next candidate + //should be considered + it = myCandidatePoints.begin(); + } + + }//end if distance below a given threshold + else return false; + + } //end while there are candidates + + return flagStop; //true if a point has been accepted + + } //end if a new point can be accepted + else return false; +} + +template +inline +void +DGtal::FMM::update(const Point& aPoint) +{ + + //neigbors + Point neighbor = aPoint; + for (Dimension k = 0; k < dimension; ++k) + { + typename Point::Coordinate c = neighbor[k]; + neighbor[k] = (c+1); + addNewCandidate(neighbor); + neighbor[k] = (c-1); + addNewCandidate(neighbor); + neighbor[k] = c; + } +} + +template +inline +bool +DGtal::FMM::addNewCandidate(const Point& aPoint) +{ + + //if it lies within the computation domain + //and if it is not already accepted + if ( (myPointPredicate(aPoint) ) + && ( myAcceptedPoints.find(aPoint) == myAcceptedPoints.end() ) ) + { + Value d = myPF( aPoint ); + PointValue newPair( aPoint, d ); + //insert the new candidate with its distance + myCandidatePoints.insert(newPair); + return true; + } + else return false; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Implementation of inline functions // + +template +inline +std::ostream& +DGtal::operator<< ( std::ostream & out, + const FMM & object ) +{ + object.selfDisplay( out ); + return out; +} + +// // +/////////////////////////////////////////////////////////////////////////////// + +/* vim: set ts=2 sw=2 : */ diff --git a/deformations/FMMPointFunctors.h b/deformations/FMMPointFunctors.h new file mode 100644 index 0000000..cbcc0d4 --- /dev/null +++ b/deformations/FMMPointFunctors.h @@ -0,0 +1,809 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +#pragma once + +/** + * @file FMMPointFunctors.h + * + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, + * France + * + * @date 2012/02/21 + * + * @brief Distance computation within a small neighborhood around a point + * + * This file is part of the DGtal library. + * + */ + +#if defined(FMMPointFunctors_RECURSES) +#error Recursive header files inclusion detected in FMMPointFunctors.h +#else // defined(FMMPointFunctors_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define FMMPointFunctors_RECURSES + +#if !defined FMMPointFunctors_h +/** Prevents repeated inclusion of headers. */ +#define FMMPointFunctors_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include +#include +#include +#include +#include "DGtal/base/Common.h" + +#include "DGtal/kernel/sets/CDigitalSet.h" +#include "DGtal/kernel/CPointFunctor.h" +#include "DGtal/images/CImage.h" +#include "DGtal/images/ImageHelper.h" +////////////////////////////////////////////////////////////////////////////// + +namespace DGtal +{ + + /////////////////////////////////////////////////////////////////////////////// + //--------------- small helpers ---------------------------------------------- + namespace details + { + //@TODO put it in a file of the base directory ? + + //comparator in absolute value + template + bool absComparator(const Value& i, const Value& j) + { + return ( std::abs(static_cast(i)) < std::abs(static_cast(j)) ); + } + + //pair second member comparator in absolute value + template + bool secondAbsComparator(const Pair& i, const Pair& j) + { + return absComparator( i.second, j.second ); + } + } + + + ///////////////////////////////////////////////////////////////////////////// + // template class L2FirstOrderLocalDistance + /** + * Description of template class 'L2FirstOrderLocalDistance'

+ * \brief Aim: Class for the computation of the Euclidean distance + * at some point p, from the available distance values of some points + * lying in the 1-neighborhood of p (ie. points at a L1-distance to p + * equal to 1). + * + * The computed value is such that the upwind gradient of the + * distance map is one, ie. it the minimum solution \f$ \Phi \f$ + * over all quadrants, verifying the following quadratic equation: + * \f$ \sum_{i = 1 \ldots d } ( \Phi - \Phi_i )^2 \f$ + * where \f$ \Phi_i \f$ is the distance value of the point preceding + * or following p along the \f$ i \f$ axis. + * + * It is a model of CPointFunctor. + * + * @tparam TImage model of CImage used for the mapping point-distance value + * @tparam TSet model of CDigitalSet for storing points whose distance value is known + * + * @see FMM + */ + template + class L2FirstOrderLocalDistance + { + + // ----------------------- Types ------------------------------ + public: + + + /// image + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + typedef TImage Image; + typedef typename Image::Point Point; + typedef typename Image::Value Value; + + /// set + BOOST_CONCEPT_ASSERT(( concepts::CDigitalSet )); + typedef TSet Set; + BOOST_STATIC_ASSERT(( boost::is_same< Point, typename TSet::Point >::value )); + + private: + + typedef std::vector Values; + + // ----------------------- Data ------------------------------------- + public: + /// Aliasing pointer on the underlying image + Image* myImgPtr; + /// Aliasing pointer on the underlying set + Set* mySetPtr; + + + // ----------------------- Interface -------------------------------------- + public: + + /** + * Constructor from an image and a set. + * NB: only pointers are stored + * + * @param aImg any distance map + * @param aSet any digital set + */ + L2FirstOrderLocalDistance(Image& aImg, TSet& aSet); + + /** + * Copy constructor. + * @param other the object to clone. + */ + L2FirstOrderLocalDistance ( const L2FirstOrderLocalDistance & other ); + + /** + * Assignment. + * @param other the object to copy. + * @return a reference on 'this'. + */ + L2FirstOrderLocalDistance & operator= ( const L2FirstOrderLocalDistance & other); + + /** + * Destructor. + * Does nothing. + */ + ~L2FirstOrderLocalDistance(); + + /** + * Euclidean distance computation at @a aPoint , + * from the available distance values + * of the 1-neighbors of @a aPoint . + * + * @param aPoint the point for which the distance is computed + * + * @return the distance value at @a aPoint. + * + */ + Value operator() (const Point& aPoint); + + /** + * Writes/Displays the object on an output stream. + * @param out the output stream where the object is written. + */ + void selfDisplay ( std::ostream & out ) const; + + // ----------------------- Internals ------------------------------------- + + private: + + /** + * Returns an approximation of the Euclidean distance + * at some point, knowing the distance of its neighbors + * + * @param aValueList the distance of (some of) the neighbors + * @return the computed distance. + */ + Value compute(Values& aValueList) const; + + + /** + * Returns the squared euclidean norm of the gradient + * of the distance map + * + * @param aValue the distance value of the point where the gradient is computed + * @param aValueList the distance value of (some of) the neighbors + * + * @return the computed gradient norm. + */ + Value gradientNorm(const Value& aValue, const Values& aValueList) const; + }; + + ///////////////////////////////////////////////////////////////////////////// + // template class L2SecondOrderLocalDistance + /** + * Description of template class 'L2SecondOrderLocalDistance'

+ * \brief Aim: Class for the computation of the Euclidean distance + * at some point p, from the available distance values of some points + * lying in the neighborhood of p, such that only one of their + * coordinate differ from the coordinates of p by at most two. + * + * Like L2FirstOrderLocalDistance, the computed value is such that + * the upwind gradient of the distance map is one, but instead of + * using first-order accurate forward and backward differences, + * L2SecondOrderLocalDistance uses second-order accurate forward + * and backward difference whenever there are enough points whose + * distance values are known in order to evaluate these differences. + * + * It is a model of CPointFunctor. + * + * @tparam TImage model of CImage used for the mapping point-distance value + * @tparam TSet model of CDigitalSet for storing points whose distance value is known + * + * @see FMM + */ + template + class L2SecondOrderLocalDistance + { + + // ----------------------- Types ------------------------------ + public: + + + /// image + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + typedef TImage Image; + typedef typename Image::Point Point; + typedef typename Image::Value Value; + + /// set + BOOST_CONCEPT_ASSERT(( concepts::CDigitalSet )); + typedef TSet Set; + BOOST_STATIC_ASSERT(( boost::is_same< Point, typename TSet::Point >::value )); + + private: + + typedef std::pair CoeffValue; + typedef std::vector List; + + // ----------------------- Data ------------------------------------- + public: + /// Aliasing pointer on the underlying image + Image* myImgPtr; + /// Aliasing pointer on the underlying set + Set* mySetPtr; + + + // ----------------------- Interface -------------------------------------- + public: + + /** + * Constructor from an image and a set. + * NB: only pointers are stored + * + * @param aImg any distance map + * @param aSet any digital set + */ + L2SecondOrderLocalDistance(Image& aImg, TSet& aSet); + + /** + * Copy constructor. + * @param other the object to clone. + */ + L2SecondOrderLocalDistance ( const L2SecondOrderLocalDistance & other ); + + /** + * Assignment. + * @param other the object to copy. + * @return a reference on 'this'. + */ + L2SecondOrderLocalDistance & operator= ( const L2SecondOrderLocalDistance & other); + + /** + * Destructor. + * Does nothing. + */ + ~L2SecondOrderLocalDistance(); + + /** + * Euclidean distance computation at @a aPoint , + * from the available distance values + * of the 1-neighbors of @a aPoint . + * + * @param aPoint the point for which the distance is computed + * + * @return the distance value at @a aPoint. + * + */ + Value operator() (const Point& aPoint); + + /** + * Writes/Displays the object on an output stream. + * @param out the output stream where the object is written. + */ + void selfDisplay ( std::ostream & out ) const; + + // ----------------------- Internals ------------------------------------- + + private: + + /** + * Returns an approximation of the Euclidean distance + * at some point, knowing the distance of its neighbors + * + * @param aList the distance of (some of) the neighbors + * @return the computed distance. + */ + Value compute(List& aList) const; + + + /** + * Returns the squared euclidean norm of the gradient + * of the distance map + * + * @param aValue the distance value of the point where the gradient is computed + * @param aList the distance value of (some of) the neighbors + * + * @return the computed gradient norm. + */ + Value gradientNorm(const Value& aValue, const List& aList) const; + }; + + + ///////////////////////////////////////////////////////////////////////////// + // template class LInfFirstOrderLocalDistance + /** + * Description of template class 'LInfFirstOrderLocalDistance'

+ * \brief Aim: Class for the computation of the LInf-distance + * at some point p, from the available distance values of some points + * lying in the 1-neighborhood of p (ie. points at a L1-distance to p + * equal to 1). + * + * If there is only one available distance value v in the 1-neighborhood of p, + * the computed value is merely incremented or decremented. + * Otherwise, it is the maximum over all + * the available distance value in the 1-neighborhood of p. + * + * It is a model of CPointFunctor. + * + * @tparam TImage model of CImage used for the mapping point-distance value + * @tparam TSet model of CDigitalSet for storing points whose distance value is known + * + * @see FMM + */ + template + class LInfFirstOrderLocalDistance + { + // ----------------------- Types ------------------------------ + public: + + + /// image + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + typedef TImage Image; + typedef typename Image::Point Point; + typedef typename Image::Value Value; + + /// set + BOOST_CONCEPT_ASSERT(( concepts::CDigitalSet )); + typedef TSet Set; + BOOST_STATIC_ASSERT(( boost::is_same< Point, typename TSet::Point >::value )); + + private: + + typedef std::vector Values; + + // ----------------------- Data ------------------------------------- + public: + /// Aliasing pointer on the underlying image + Image* myImgPtr; + /// Aliasing pointer on the underlying set + Set* mySetPtr; + + + // ----------------------- Interface -------------------------------------- + public: + + /** + * Constructor from an image and a set. + * NB: only pointers are stored + * + * @param aImg any distance map + * @param aSet any digital set + */ + LInfFirstOrderLocalDistance(Image& aImg, TSet& aSet); + + /** + * Copy constructor. + * @param other the object to clone. + */ + LInfFirstOrderLocalDistance ( const LInfFirstOrderLocalDistance & other ); + + /** + * Assignment. + * @param other the object to copy. + * @return a reference on 'this'. + */ + LInfFirstOrderLocalDistance & operator= ( const LInfFirstOrderLocalDistance & other); + + /** + * Destructor. + * Does nothing. + */ + ~LInfFirstOrderLocalDistance(); + + + /** + * LInf-distance computation at @a aPoint , + * from the available distance values + * of the 1-neighbors of @a aPoint . + * + * @param aPoint the point for which the distance is computed + * + * @return the distance value at @a aPoint. + * + */ + Value operator() (const Point& aPoint); + + /** + * Writes/Displays the object on an output stream. + * @param out the output stream where the object is written. + */ + void selfDisplay ( std::ostream & out ) const; + + // ----------------------- Internals ------------------------------------- + + private: + + /** + * Returns the LInf-distance at some point, + * knowing the distance of its neighbors + * + * @param aValueList the distance of (some of) the neighbors + * @return the computed distance. + */ + Value compute(Values& aValueList) const; + + }; + + ///////////////////////////////////////////////////////////////////////////// + // template class L1FirstOrderLocalDistance + /** + * Description of template class 'L1FirstOrderLocalDistance'

+ * \brief Aim: Class for the computation of the L1-distance + * at some point p, from the available distance values of some points + * lying in the 1-neighborhood of p (ie. points at a L1-distance to p + * equal to 1). + * + * The computed value is merely the minimum over all + * the available distance values in the 1-neighborhood of p, + * plus one. + * + * It is a model of CPointFunctor. + * + * @tparam TImage model of CImage used for the mapping point-distance value + * @tparam TSet model of CDigitalSet for storing points whose distance value is known + * + * @see FMM + */ + template + class L1FirstOrderLocalDistance + { + // ----------------------- Types ------------------------------ + public: + + + /// image + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + typedef TImage Image; + typedef typename Image::Point Point; + typedef typename Image::Value Value; + + /// set + BOOST_CONCEPT_ASSERT(( concepts::CDigitalSet )); + typedef TSet Set; + BOOST_STATIC_ASSERT(( boost::is_same< Point, typename TSet::Point >::value )); + + private: + + typedef std::vector Values; + + // ----------------------- Data ------------------------------------- + public: + /// Aliasing pointer on the underlying image + Image* myImgPtr; + /// Aliasing pointer on the underlying set + Set* mySetPtr; + + // ----------------------- Interface -------------------------------------- + public: + + /** + * Constructor from an image and a set. + * NB: only pointers are stored + * + * @param aImg any distance map + * @param aSet any digital set + */ + L1FirstOrderLocalDistance(Image& aImg, TSet& aSet); + + /** + * Copy constructor. + * @param other the object to clone. + */ + L1FirstOrderLocalDistance ( const L1FirstOrderLocalDistance & other ); + + /** + * Assignment. + * @param other the object to copy. + * @return a reference on 'this'. + */ + L1FirstOrderLocalDistance & operator= ( const L1FirstOrderLocalDistance & other); + + /** + * Destructor. + * Does nothing. + */ + ~L1FirstOrderLocalDistance(); + + /** + * L1-distance computation at @a aPoint , + * from the available distance values + * of the 1-neighbors of @a aPoint . + * + * @param aPoint the point for which the distance is computed + * + * @return the distance value at @a aPoint. + */ + Value operator() (const Point& aPoint); + + /** + * Writes/Displays the object on an output stream. + * @param out the output stream where the object is written. + */ + void selfDisplay ( std::ostream & out ) const; + + // ----------------------- Internals ------------------------------------- + + private: + + /** + * Returns the L1-distance at some point, + * knowing the distance of its neighbors + * + * @param aValueList the distance of (some of) the neighbors + * @return the computed distance. + */ + Value compute(Values& aValueList) const; + + }; + + + ///////////////////////////////////////////////////////////////////////////// + // template class L2FirstOrderLocalDistanceFromCells + /** + * Description of template class 'L2FirstOrderLocalDistanceFromCells'

+ * \brief Aim: Class for the computation of the Euclidean distance + * at some point p, from the available distance values in the neighborhood of p. + * Contrary to L2FirstOrderLocalDistance, the distance values are not available + * from the points adjacent to p but instead from the (d-1)-cells lying between p + * and these points. + * + * @note The stored values are expected to be the distance of the interface + * to the points directly incident to the cells and must be between 0 and 1. + * + * @tparam TKSpace a model of cellular grid + * @tparam TMap a model of associative container used for the mapping cells-value + * @tparam isIndirect a bool equal to 'false' if the tested points are expected to be + * directly incident to the cells (default) and 'true' otherwise + * + * @see initFromBelsRange FMM + */ + template + class L2FirstOrderLocalDistanceFromCells + { + + // ----------------------- Types ------------------------------ + public: + + + /// map + typedef TMap Map; + typedef typename Map::mapped_type Value; + + /// cellular grid + typedef TKSpace KSpace; + typedef typename KSpace::Point Point; + typedef typename KSpace::Cell Cell; + + private: + + typedef std::vector Values; + + // ----------------------- Data ------------------------------------- + public: + /// Aliasing pointer on the underlying cellular grid + const KSpace* myKSpace; + /// Aliasing pointer on the underlying mapping + Map* myMap; + + // ----------------------- Interface -------------------------------------- + public: + + /** + * Constructor from a space and a map. + * NB: only pointers are stored + * + * @param aMap any distance map + */ + L2FirstOrderLocalDistanceFromCells(const KSpace& aK, Map& aMap); + + /** + * Copy constructor. + * @param other the object to clone. + */ + L2FirstOrderLocalDistanceFromCells ( const L2FirstOrderLocalDistanceFromCells & other ); + + /** + * Assignment. + * @param other the object to copy. + * @return a reference on 'this'. + */ + L2FirstOrderLocalDistanceFromCells & operator= ( const L2FirstOrderLocalDistanceFromCells & other); + + /** + * Destructor. + * Does nothing. + */ + ~L2FirstOrderLocalDistanceFromCells(); + + /** + * Euclidean distance computation at @a aPoint , + * from the available distance values + * of the adjacent cells. + * + * @param aPoint the point for which the distance is computed + * + * @return the distance value at @a aPoint. + * + */ + Value operator() (const Point& aPoint); + + /** + * Writes/Displays the object on an output stream. + * @param out the output stream where the object is written. + */ + void selfDisplay ( std::ostream & out ) const; + + // ----------------------- Internals ------------------------------------- + + private: + + /** + * Returns an approximation of the Euclidean distance + * at some point, knowing the distance of its adjacent cells + * contained in @a aValueList + * + * @param aValueList the distance of (some of) the neighbors + * @return the computed distance. + */ + Value compute(Values& aValueList) const; + + }; + + + ///////////////////////////////////////////////////////////////////////////// + // template class SpeedExtrapolator + /** + * Description of template class 'SpeedExtrapolator'

+ * \brief Aim: Class for the computation of the a speed value + * at some point p, from the available distance values and speed + * values of some points lying in the 1-neighborhood of p + * (ie. points at a L1-distance to p equal to 1) in order to + * extrapolate a speed field in the normal direction to the interface. + * + * The computed value is such that the dot product of the gradients + * of the speed function and of the distance function is zero, ie. + * \f$ \nabla S . \nabla \Phi = 0 \f$. + * + * [Adalsteinsson and Sethian, Fast Construction of Extension Velocities + * in Level Set Methods, J. Comput. Phys. 148, 2-22, 1999] + * + * It is a model of CPointFunctor. + * + * @tparam TDistanceImage model of CImage used for the mapping point-distance value + * @tparam TSet model of CDigitalSet for storing points whose distance value is known + * @tparam TSpeedFunctor model of CImage used for the mapping point-speed value + * + * @see FMM + */ + template + class SpeedExtrapolator + { + + // ----------------------- Types ------------------------------ + public: + + + /// image + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + typedef TDistanceImage DistanceImage; + typedef typename DistanceImage::Point Point; + typedef typename DistanceImage::Value DistanceValue; + BOOST_CONCEPT_ASSERT(( concepts::CPointFunctor )); + typedef TSpeedFunctor SpeedFunctor; + BOOST_STATIC_ASSERT(( boost::is_same< Point, typename SpeedFunctor::Point >::value )); + typedef typename SpeedFunctor::Value Value; + + /// set + BOOST_CONCEPT_ASSERT(( concepts::CDigitalSet )); + typedef TSet Set; + BOOST_STATIC_ASSERT(( boost::is_same< Point, typename TSet::Point >::value )); + + // ----------------------- Data ------------------------------------- + public: + /// Aliasing pointer on the underlying image of distance values + const DistanceImage* myDistImgPtr; + /// Aliasing pointer on the underlying set of points + /// whose distance value is known + const Set* mySetPtr; + /// Aliasing pointer on the underlying image of speed values + DistanceImage* mySpeedFuncPtr; + + + // ----------------------- Interface -------------------------------------- + public: + + /** + * Constructor from images and set. + * NB: only pointers are stored + * + * @param aDistImg any distance map + * @param aSet any digital set + * @param aSpeedFunc any speed map + */ + SpeedExtrapolator(const DistanceImage& aDistImg, const TSet& aSet, SpeedFunctor& aSpeedFunc); + + /** + * Copy constructor. + * @param other the object to clone. + */ + SpeedExtrapolator ( const SpeedExtrapolator & other ); + + /** + * Assignment. + * @param other the object to copy. + * @return a reference on 'this'. + */ + SpeedExtrapolator & operator= ( const SpeedExtrapolator & other); + + /** + * Destructor. + * Does nothing. + */ + ~SpeedExtrapolator(); + + /** + * Speed value computation at @a aPoint , + * from the available distance/speed values + * of the 1-neighbors of @a aPoint . + * + * @param aPoint the point for which the speed is computed + * + * @return the speed value at @a aPoint. + * + */ + Value operator() (const Point& aPoint); + + + // ----------------------- Internals ------------------------------------- + + private: + + }; + + +} // namespace DGtal + + +/////////////////////////////////////////////////////////////////////////////// +// Includes inline functions. +#include "FMMPointFunctors.ih" + +// // +/////////////////////////////////////////////////////////////////////////////// + +#endif // !defined FMMPointFunctors_h + +#undef FMMPointFunctors_RECURSES +#endif // else defined(FMMPointFunctors_RECURSES) diff --git a/deformations/FMMPointFunctors.ih b/deformations/FMMPointFunctors.ih new file mode 100644 index 0000000..9dabf45 --- /dev/null +++ b/deformations/FMMPointFunctors.ih @@ -0,0 +1,1007 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +/** + * @file FMMPointFunctors.ih + * @author Tristan Roussillon (\c + * tristan.roussillon@liris.cnrs.fr ) Laboratoire d'InfoRmatique en + * Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, + * France + * + * + * @date 2012/02/21 + * + * @brief Implementation of inline methods defined in FMMPointFunctors.h + * + * This file is part of the DGtal library. + */ + + +////////////////////////////////////////////////////////////////////////////// +#include +////////////////////////////////////////////////////////////////////////////// + + + +/////////////////////////////////////////////////////////////////////////////// +// IMPLEMENTATION of inline methods. +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- +template +inline +DGtal::L2FirstOrderLocalDistance::L2FirstOrderLocalDistance + (Image& aImg, TSet& aSet): myImgPtr(&aImg), mySetPtr(&aSet) +{ +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::L2FirstOrderLocalDistance::L2FirstOrderLocalDistance + (const L2FirstOrderLocalDistance& other): myImgPtr(other.myImgPtr), mySetPtr(other.mySetPtr) +{ +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::L2FirstOrderLocalDistance& +DGtal::L2FirstOrderLocalDistance::operator= + (const L2FirstOrderLocalDistance& other) +{ + if( this != &other) + { + myImgPtr = other.myImgPtr; + mySetPtr = other.mySetPtr; + } + return *this; +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::L2FirstOrderLocalDistance::~L2FirstOrderLocalDistance + () +{ +} + +//----------------------------------------------------------------------------- +template +inline +typename DGtal::L2FirstOrderLocalDistance::Value +DGtal::L2FirstOrderLocalDistance::operator() + (const Point& aPoint) +{ + + //distance values + Values v; + v.reserve(Point::dimension); + + //two 1-neighbors + Point neighbor1 = aPoint; + Point neighbor2 = aPoint; + + typename Point::Iterator it1 = neighbor1.begin(); + typename Point::Iterator it2 = neighbor2.begin(); + typename Point::ConstIterator it = aPoint.begin(); + typename Point::ConstIterator itEnd = aPoint.end(); + for ( ; it != itEnd; ++it, ++it1, ++it2) + {//for each dimension + + typename Point::Coordinate c = *it; + *it1 = (c+1); + *it2 = (c-1); + + //neighboring values + Value d, d1, d2 = 0; + bool flag1 = findAndGetValue( *myImgPtr, *mySetPtr, neighbor1, d1 ); + bool flag2 = findAndGetValue( *myImgPtr, *mySetPtr, neighbor2, d2 ); + if ( flag1 || flag2 ) + { + if ( flag1 && flag2 ) + { //take the minimal value + if (std::abs(d1) < std::abs(d2)) + d = d1; + else + d = d2; + } else + { + if (flag1) d = d1; + if (flag2) d = d2; + } + + v.push_back(d); + } + + *it1 = c; + *it2 = c; + } //end for each dimension + + //computation of the new value + return this->compute(v); +} + + +//----------------------------------------------------------------------------- +template +inline +typename DGtal::L2FirstOrderLocalDistance::Value +DGtal::L2FirstOrderLocalDistance::compute +(Values& aValueList) const +{ + ASSERT(aValueList.size() > 0); + + unsigned int nb = aValueList.size(); + if ( nb == 1 ) + { + Value d = aValueList.back(); + if (d > 0) return d + 1.0; + else return d - 1.0; + } + else + { + //function computation + typename Values::iterator itMax = + std::max_element( aValueList.begin(), aValueList.end(), details::absComparator ); + if ( gradientNorm( *itMax, aValueList ) > 1 ) + { + aValueList.erase( itMax ); + return this->compute(aValueList); + } + else + { //resolution + double a = 0; + double b = 0; + double c = -1; + + for (typename Values::iterator it = aValueList.begin(); + it != aValueList.end(); ++it) + { + Value d = *it; + + a += 1; + b -= static_cast(2*d); + c += static_cast(d*d); + } + + //discriminant + double disc = b*b - 4*a*c; + ASSERT(disc >= 0); + + if ( b < 0 ) + return static_cast( ( -b + std::sqrt(disc) ) / (2*a) ); + else + return static_cast( ( -b - std::sqrt(disc) ) / (2*a) ); + } + } + +} + +//----------------------------------------------------------------------------- +template +inline +typename DGtal::L2FirstOrderLocalDistance::Value +DGtal::L2FirstOrderLocalDistance +::gradientNorm(const Value& aValue, const Values& aValueList) const +{ + double sum = 0; + for (typename Values::const_iterator it = aValueList.begin(); + it != aValueList.end(); ++it) + { + Value d = (aValue - *it); + sum += (d*d); + } + return sum; +} + +//----------------------------------------------------------------------------- +template +inline +void +DGtal::L2FirstOrderLocalDistance +::selfDisplay ( std::ostream & out ) const +{ + out << "L2"; +} + +/////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- +template +inline +DGtal::L2SecondOrderLocalDistance::L2SecondOrderLocalDistance + (Image& aImg, TSet& aSet): myImgPtr(&aImg), mySetPtr(&aSet) +{ +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::L2SecondOrderLocalDistance::L2SecondOrderLocalDistance + (const L2SecondOrderLocalDistance& other): myImgPtr(other.myImgPtr), mySetPtr(other.mySetPtr) +{ +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::L2SecondOrderLocalDistance& +DGtal::L2SecondOrderLocalDistance::operator= + (const L2SecondOrderLocalDistance& other) +{ + if( this != &other) + { + myImgPtr = other.myImgPtr; + mySetPtr = other.mySetPtr; + } + return *this; +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::L2SecondOrderLocalDistance::~L2SecondOrderLocalDistance + () +{ +} + +//----------------------------------------------------------------------------- +template +inline +typename DGtal::L2SecondOrderLocalDistance::Value +DGtal::L2SecondOrderLocalDistance::operator() + (const Point& aPoint) +{ + + //distance values + List l; + l.reserve(Point::dimension); + + //two 1-neighbors + Point neighbor1 = aPoint; + Point neighbor2 = aPoint; + + typename Point::Iterator it1 = neighbor1.begin(); + typename Point::Iterator it2 = neighbor2.begin(); + typename Point::ConstIterator it = aPoint.begin(); + typename Point::ConstIterator itEnd = aPoint.end(); + for ( ; it != itEnd; ++it, ++it1, ++it2) + {//for each dimension + + typename Point::Coordinate c = *it; + + /// first-order + double coeff = 1.0; + ++(*it1); + --(*it2); + Value d, d1, d2 = 0; + bool flag1 = findAndGetValue( *myImgPtr, *mySetPtr, neighbor1, d1 ); + bool flag2 = findAndGetValue( *myImgPtr, *mySetPtr, neighbor2, d2 ); + if ( flag1 || flag2 ) + { + /// second-order + ++(*it1); + --(*it2); + Value d12, d22 = 0; + bool flag12 = findAndGetValue( *myImgPtr, *mySetPtr, neighbor1, d12 ); + bool flag22 = findAndGetValue( *myImgPtr, *mySetPtr, neighbor2, d22 ); + + if ( flag1 && flag2 ) + { + + if ( flag12 && flag22 ) + { //take the minimal value + d1 = 2.0*d1 - d12/2.0; + d2 = 2.0*d2 - d22/2.0; + if (std::abs(d1) < std::abs(d2)) + d = d1; + else + d = d2; + coeff = 1.5; + } + else + { //like first-order accurate case + //take the minimal value + if (std::abs(d1) < std::abs(d2)) + d = d1; + else + d = d2; + } + + } + else //if not flag1 AND flag2 both true + { + if (flag1) + { + + if ( flag12 ) + { + d1 = 2.0*d1 - d12/2.0; + coeff = 1.5; + } + d = d1; + + } + + if (flag2) + { + + if ( flag22 ) + { + d2 = 2.0*d2 - d22/2.0; + coeff = 1.5; + } + d = d2; + } + } + + l.push_back( CoeffValue( coeff, d ) ); + + } //end if flag1 or flag2 + + *it1 = c; + *it2 = c; + } //end for each dimension + + //computation of the new value + return this->compute(l); +} + + +//----------------------------------------------------------------------------- +template +inline +typename DGtal::L2SecondOrderLocalDistance::Value +DGtal::L2SecondOrderLocalDistance::compute +(List& aList) const +{ + ASSERT(aList.size() > 0); + + unsigned int nb = aList.size(); + if ( nb == 1 ) + { + CoeffValue pair = aList.back(); + if (pair.second > 0) + return ( (pair.second + 1.0)/(pair.first) ); + else + return ( (pair.second - 1.0)/(pair.first) ); + } + else + { + //function computation + // typename List::iterator itMax = + // std::max_element( aList.begin(), aList.end(), details::secondAbsComparator ); + // if ( gradientNorm( itMax->second, aList ) > 1 ) + // { + // trace.info() << "NB. numerical error in L2SecondOrderLocalDistance " << std::endl; + // aList.erase( itMax ); + // return this->compute(aList); + // } + // else + { //resolution + double a = 0; + double b = 0; + double c = -1; + + for (typename List::iterator it = aList.begin(); + it != aList.end(); ++it) + { + double coeff = it->first; + Value v = it->second; + + a += (coeff*coeff); + b -= 2 * coeff * static_cast(v); + c += static_cast(v*v); + } + + //discriminant + double disc = b*b - 4*a*c; + ASSERT(disc >= 0); + + if ( b < 0 ) + return static_cast( ( -b + std::sqrt(disc) ) / (2*a) ); + else + return static_cast( ( -b - std::sqrt(disc) ) / (2*a) ); + } + } +} + +//----------------------------------------------------------------------------- +template +inline +typename DGtal::L2SecondOrderLocalDistance::Value +DGtal::L2SecondOrderLocalDistance +::gradientNorm(const Value& aValue, const List& aList) const +{ + double sum = 0; + for (typename List::const_iterator it = aList.begin(); + it != aList.end(); ++it) + { + Value v = std::abs( it->first*aValue - it->second ); + sum += (v*v); + } + return sum; +} + +//----------------------------------------------------------------------------- +template +inline +void +DGtal::L2SecondOrderLocalDistance +::selfDisplay ( std::ostream & out ) const +{ + out << "L2"; +} + +/////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- +template +inline +DGtal::LInfFirstOrderLocalDistance::LInfFirstOrderLocalDistance + (Image& aImg, TSet& aSet): myImgPtr(&aImg), mySetPtr(&aSet) +{ +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::LInfFirstOrderLocalDistance::LInfFirstOrderLocalDistance + (const LInfFirstOrderLocalDistance& other): myImgPtr(other.myImgPtr), mySetPtr(other.mySetPtr) +{ +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::LInfFirstOrderLocalDistance& +DGtal::LInfFirstOrderLocalDistance::operator= + (const LInfFirstOrderLocalDistance& other) +{ + if( this != &other) + { + myImgPtr = other.myImgPtr; + mySetPtr = other.mySetPtr; + } + return *this; +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::LInfFirstOrderLocalDistance::~LInfFirstOrderLocalDistance + () +{ +} +//----------------------------------------------------------------------------- +template +inline +typename DGtal::LInfFirstOrderLocalDistance::Value +DGtal::LInfFirstOrderLocalDistance::operator() + (const Point& aPoint) +{ + + //distance values + Values v; + v.reserve(Point::dimension); + + //two 1-neighbors + Point neighbor1 = aPoint; + Point neighbor2 = aPoint; + + typename Point::Iterator it1 = neighbor1.begin(); + typename Point::Iterator it2 = neighbor2.begin(); + typename Point::ConstIterator it = aPoint.begin(); + typename Point::ConstIterator itEnd = aPoint.end(); + for ( ; it != itEnd; ++it, ++it1, ++it2) + {//for each dimension + + typename Point::Coordinate c = *it; + *it1 = (c+1); + *it2 = (c-1); + + //neighboring values + Value d, d1, d2 = 0; + bool flag1 = findAndGetValue( *myImgPtr, *mySetPtr, neighbor1, d1 ); + bool flag2 = findAndGetValue( *myImgPtr, *mySetPtr, neighbor2, d2 ); + if ( flag1 || flag2 ) + { + if ( flag1 && flag2 ) + { //take the minimal value + if (std::abs(d1) < std::abs(d2)) + d = d1; + else + d = d2; + } else + { + if (flag1) d = d1; + if (flag2) d = d2; + } + + v.push_back(d); + + } + + *it1 = c; + *it2 = c; + } //end for each dimension + + //computation of the new value + return this->compute(v); + +} + +//----------------------------------------------------------------------------- +template +inline +typename DGtal::LInfFirstOrderLocalDistance::Value +DGtal::LInfFirstOrderLocalDistance::compute +(Values& aValueList) const +{ + + ASSERT(aValueList.size() > 0); + + unsigned int nb = aValueList.size(); + if ( nb == 1 ) + { + Value d = aValueList.back(); + if (d >= 0) ++d; + else --d; + return d; + } + else + { //max element + typename Values::iterator it = + std::max_element( aValueList.begin(), aValueList.end(), details::absComparator ); + return *it; + } +} + +//----------------------------------------------------------------------------- +template +inline +void +DGtal::LInfFirstOrderLocalDistance +::selfDisplay ( std::ostream & out ) const +{ + out << "LInf"; +} + +/////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- +template +inline +DGtal::L1FirstOrderLocalDistance::L1FirstOrderLocalDistance + (Image& aImg, TSet& aSet): myImgPtr(&aImg), mySetPtr(&aSet) +{ +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::L1FirstOrderLocalDistance::L1FirstOrderLocalDistance + (const L1FirstOrderLocalDistance& other): myImgPtr(other.myImgPtr), mySetPtr(other.mySetPtr) +{ +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::L1FirstOrderLocalDistance& +DGtal::L1FirstOrderLocalDistance::operator= + (const L1FirstOrderLocalDistance& other) +{ + if( this != &other) + { + myImgPtr = other.myImgPtr; + mySetPtr = other.mySetPtr; + } + return *this; +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::L1FirstOrderLocalDistance::~L1FirstOrderLocalDistance + () +{ +} +//----------------------------------------------------------------------------- +template +inline +typename DGtal::L1FirstOrderLocalDistance::Value +DGtal::L1FirstOrderLocalDistance::operator() + (const Point& aPoint) +{ + + //distance values + Values v; + v.reserve(2*Point::dimension); + + //two 1-neighbors + Point neighbor1 = aPoint; + Point neighbor2 = aPoint; + + typename Point::Iterator it1 = neighbor1.begin(); + typename Point::Iterator it2 = neighbor2.begin(); + typename Point::ConstIterator it = aPoint.begin(); + typename Point::ConstIterator itEnd = aPoint.end(); + for ( ; it != itEnd; ++it, ++it1, ++it2) + {//for each dimension + + typename Point::Coordinate c = *it; + *it1 = (c+1); + *it2 = (c-1); + + //neighboring values + Value d1, d2 = 0; + bool flag1 = findAndGetValue( *myImgPtr, *mySetPtr, neighbor1, d1 ); + bool flag2 = findAndGetValue( *myImgPtr, *mySetPtr, neighbor2, d2 ); + if (flag1) v.push_back( d1 ); + if (flag2) v.push_back( d2 ); + + *it1 = c; + *it2 = c; + } //end for each dimension + + //computation of the new value + return this->compute(v); + +} + +//----------------------------------------------------------------------------- +template +inline +typename DGtal::L1FirstOrderLocalDistance::Value +DGtal::L1FirstOrderLocalDistance::compute +(Values& aValueList) const +{ + ASSERT(aValueList.size() > 0); + + //min (in absolute values) + typename Values::iterator it = + std::min_element( aValueList.begin(), aValueList.end(), details::absComparator ); + Value vmin = *it; + + //sign + if (vmin >= 0) + return vmin + 1; + else + return vmin - 1; +} + +//----------------------------------------------------------------------------- +template +inline +void +DGtal::L1FirstOrderLocalDistance +::selfDisplay ( std::ostream & out ) const +{ + out << "L1"; +} +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Helper classes defined in the compilation unit (anonymous namespace) +// see operator() below +namespace +{ + // + template + struct ValueBetween0And1 + { + template + static Value get(const Value& v) + { + ASSERT( (v>=0)&&(v<=1) ); + return v; + } + }; + //specialization + template< > + struct ValueBetween0And1 + { + template + static Value get(const Value& v) + { + ASSERT( (v>=0)&&(v<=1) ); + return (1 - v); + } + }; +} + +/////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- +template +inline +DGtal::L2FirstOrderLocalDistanceFromCells +::L2FirstOrderLocalDistanceFromCells + (const KSpace& aK, Map& aMap): myKSpace(&aK), myMap(&aMap) +{ +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::L2FirstOrderLocalDistanceFromCells +::L2FirstOrderLocalDistanceFromCells + (const L2FirstOrderLocalDistanceFromCells& other) +: myKSpace(other.myKSpace), myMap(other.myMap) +{ +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::L2FirstOrderLocalDistanceFromCells& +DGtal::L2FirstOrderLocalDistanceFromCells +::operator= + (const L2FirstOrderLocalDistanceFromCells& other) +{ + if( this != &other) + { + myMap = other.myMap; + myKSpace = other.myKSpace; + } + return *this; +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::L2FirstOrderLocalDistanceFromCells +::~L2FirstOrderLocalDistanceFromCells + () +{ +} + +//----------------------------------------------------------------------------- +template +inline +typename DGtal::L2FirstOrderLocalDistanceFromCells::Value +DGtal::L2FirstOrderLocalDistanceFromCells::operator() + (const Point& aPoint) +{ + + //distance values + Values v; + v.reserve(Point::dimension); + + Cell spel = myKSpace->uSpel( aPoint ); + for ( typename KSpace::DirIterator q = myKSpace->uDirs( spel ); + (q != 0); ++q ) + { //for each dimension + const DGtal::Dimension dir = *q; + + /// for the direct orientation + Cell surfel1 = myKSpace->uIncident( spel, dir, true ); + //ASSERT( myKSpace->uIsSurfel( surfel1 ) ); + ASSERT( myKSpace->uDim( surfel1 ) == (KSpace::dimension - 1) ); + + /// for the indirect orientation + Cell surfel2 = myKSpace->uIncident( spel, dir, false ); + //ASSERT( myKSpace->uIsSurfel( surfel2 ) ); + ASSERT( myKSpace->uDim( surfel2 ) == (KSpace::dimension - 1) ); + + //neighboring values + Value d = 0; + typename Map::iterator it1 = myMap->find( surfel1 ); + typename Map::iterator it2 = myMap->find( surfel2 ); + bool flag1 = ( it1 != myMap->end() ); + bool flag2 = ( it2 != myMap->end() ); + if ( flag1 || flag2 ) + { + if ( flag1 && flag2 ) + { //take the minimal value + ASSERT( (it1->second >= 0)&&(it1->second <= 1) ); + ASSERT( (it2->second >= 0)&&(it2->second <= 1) ); + if (it1->second < it2->second) + d = ValueBetween0And1 + ::get( it1->second ); + else + d = ValueBetween0And1 + ::get( it2->second ); + } else + { + if (flag1) + { + ASSERT( (it1->second >= 0)&&(it1->second <= 1) ); + d = ValueBetween0And1 + ::get( it1->second ); + } + if (flag2) + { + ASSERT( (it2->second >= 0)&&(it2->second <= 1) ); + d = ValueBetween0And1 + ::get( it2->second ); + } + } + + if (d == 0) return 0; + + v.push_back(d); + } + + } //end for each dimension + + //computation of the new value + return this->compute(v); +} + + +//----------------------------------------------------------------------------- +template +inline +typename DGtal::L2FirstOrderLocalDistanceFromCells::Value +DGtal::L2FirstOrderLocalDistanceFromCells::compute +(Values& aValueList) const +{ + ASSERT(aValueList.size() > 0); + + unsigned int nb = aValueList.size(); + if ( nb == 1 ) + { + return aValueList.back(); + } + else + { //resolution + double a = 0; + + for (typename Values::iterator it = aValueList.begin(); + it != aValueList.end(); ++it) + { + Value d = *it; + a += ( 1.0 / static_cast( d*d ) ); + } + + return static_cast( std::sqrt(a) / a ); + } +} + +//----------------------------------------------------------------------------- +template +inline +void +DGtal::L2FirstOrderLocalDistanceFromCells +::selfDisplay ( std::ostream & out ) const +{ + out << "L2"; +} + +/////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- +template +inline +DGtal::SpeedExtrapolator::SpeedExtrapolator +(const DistanceImage& aDistImg, const Set& aSet, SpeedFunctor& aSpeedFunc) + : myDistImgPtr(&aDistImg), mySetPtr(&aSet), mySpeedFuncPtr(&aSpeedFunc) +{ +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::SpeedExtrapolator::SpeedExtrapolator + (const SpeedExtrapolator& other) + : myDistImgPtr(other.myDistImgPtr), mySetPtr(other.mySetPtr), mySpeedFuncPtr(other.mySpeedFuncPtr) +{ +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::SpeedExtrapolator& +DGtal::SpeedExtrapolator::operator= + (const SpeedExtrapolator& other) +{ + if( this != &other) + { + myDistImgPtr = other.myDistImgPtr; + mySetPtr = other.mySetPtr; + mySpeedFuncPtr = other.mySpeedFuncPtr; + } + return *this; +} + +//----------------------------------------------------------------------------- +template +inline +DGtal::SpeedExtrapolator::~SpeedExtrapolator + () +{ +} + +//----------------------------------------------------------------------------- +template +inline +typename DGtal::SpeedExtrapolator::Value +DGtal::SpeedExtrapolator::operator() + (const Point& aPoint) +{ + + //speed values + Value num, den = 0; + + //two 1-neighbors for one dimension + Point neighbor1 = aPoint; + Point neighbor2 = aPoint; + + typename Point::Iterator it1 = neighbor1.begin(); + typename Point::Iterator it2 = neighbor2.begin(); + typename Point::ConstIterator it = aPoint.begin(); + typename Point::ConstIterator itEnd = aPoint.end(); + for ( ; it != itEnd; ++it, ++it1, ++it2) + {//for each dimension + + typename Point::Coordinate c = *it; + + //distance value + DistanceValue d = 0; + bool flag = findAndGetValue( *myDistImgPtr, *mySetPtr, aPoint, d ); + ASSERT( flag ); + + //neighbors + *it1 = (c+1); + *it2 = (c-1); + //neighboring speed value + Value s = 0; + //neighboring distance values + DistanceValue d0, d1, d2 = 0; + bool flag1 = findAndGetValue( *myDistImgPtr, *mySetPtr, neighbor1, d1 ); + bool flag2 = findAndGetValue( *myDistImgPtr, *mySetPtr, neighbor2, d2 ); + if ( flag1 || flag2 ) + { + if ( flag1 && flag2 ) + { //take the minimal value + if (std::abs(d1) < std::abs(d2)) + { + d0 = d1; + s = (*mySpeedFuncPtr)( neighbor1 ); + } + else + { + d0 = d2; + s = (*mySpeedFuncPtr)( neighbor2 ); + } + } else + { + if (flag1) + { + d0 = d1; + s = (*mySpeedFuncPtr)( neighbor1 ); + } + if (flag2) + { + d0 = d2; + s = (*mySpeedFuncPtr)( neighbor2 ); + } + } + + Value diff = static_cast(d - d0); + num += ( s * diff ); + den += diff; + } + + *it1 = c; + *it2 = c; + } //end for each dimension + + //computation of the new value + ASSERT(den != 0); + return (num/den); +} diff --git a/deformations/FrontierEvolver.h b/deformations/FrontierEvolver.h new file mode 100644 index 0000000..30eb6c7 --- /dev/null +++ b/deformations/FrontierEvolver.h @@ -0,0 +1,459 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +#pragma once + +/** + * @file FrontierEvolver.h + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * + * @date 2012/03/01 + * + * Header file for module FrontierEvolver.cpp + * + * This file is part of the DGtal library. + */ + +#if defined(FrontierEvolver_RECURSES) +#error Recursive header files inclusion detected in FrontierEvolver.h +#else // defined(FrontierEvolver_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define FrontierEvolver_RECURSES + +#if !defined FrontierEvolver_h +/** Prevents repeated inclusion of headers. */ +#define FrontierEvolver_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include + +#include "DGtal/base/Common.h" +#include "DGtal/images/CImage.h" +#include "DGtal/kernel/CPointFunctor.h" +#include "DGtal/kernel/CPointPredicate.h" + +//predicates +#include "DGtal/base/BasicBoolFunctors.h" +#include "DGtal/kernel/BasicPointPredicates.h" +#include "PointPredicates.h" + + + +// set +#include "DGtal/kernel/sets/DigitalSetFromMap.h" +#include "DGtal/kernel/sets/DigitalSetBySTLSet.h" +#include "DGtal/kernel/sets/DigitalSetInserter.h" +#include "DGtal/images/ImageHelper.h" + +// FMM +#include "FMM.h" + +// frontier +#include "DGtal/topology/SurfelAdjacency.h" +#include "DGtal/topology/helpers/FrontierPredicate.h" +#include "DGtal/topology/LightExplicitDigitalSurface.h" + +//partition +#include "PartitionEvolver.h" +#include "FrontierEvolverHelpers.h" +////////////////////////////////////////////////////////////////////////////// + +namespace DGtal +{ + + + ///////////////////////////////////////////////////////////////////////////// + // template class FrontierEvolver + /** + * Description of template class 'FrontierEvolver'

+ * \brief Aim: This class is a way of deforming an image of labels + * around a connected contact surface between two regions, + * according to a speed field, whose computation is + * delegated to a point functor. + * + * At each step, a implicit function is extended from the known + * values at the boundary points adjacent to the contact surface. + * The points lying around the interface are sorted according to + * their time of zero-crossing (ie. their distance to the interface + * divided by their speed) so that they are flipped from one region + * to another, one by one and in order, until a time greater than + * a given threshold is reached, but only if a given predicate + * based on topological properties returns true. + * + * @tparam TKSpace a model of CCellularGridSpaceND + * @tparam TLabelImage a model of CImage (storing labels) + * @tparam TDistanceImage a model of CImage (storing distance values) + * @tparam TFunctor a model of CPointFunctor + * @tparam TTopoPredicate a model of topological predicate + */ + template + class FrontierEvolver + { + + + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + BOOST_STATIC_ASSERT + (( ConceptUtils::SameType< typename TKSpace::Point, + typename TLabelImage::Point>::value )); + BOOST_STATIC_ASSERT + (( ConceptUtils::SameType< typename TKSpace::Point, + typename TDistanceImage::Point>::value )); + + BOOST_CONCEPT_ASSERT(( concepts::CPointFunctor )); + BOOST_STATIC_ASSERT + (( ConceptUtils::SameType< typename TKSpace::Point, + typename TFunctor::Point>::value )); + + // BOOST_CONCEPT_ASSERT(( concepts::CPointPredicate )); + // BOOST_STATIC_ASSERT + // (( ConceptUtils::SameType< typename TKSpace::Point, + // typename TTopoPredicate::Point>::value )); + //TODO testing TTopoPredicate as a binary predicate on points and labels + + // ----------------------- Types ------------------------------ + public: + + /// Image of labels + typedef TLabelImage LImage; + typedef typename LImage::Value Label; + typedef typename LImage::Domain Domain; + typedef typename Domain::Point Point; + + /// Image of distance values + typedef TDistanceImage DImage; + typedef typename DImage::Value Distance; + + /// Point functor for the mapping points-speed + typedef TFunctor Functor; + typedef typename Functor::Value Speed; + typedef std::pair DistanceSpeed; + typedef std::pair PointTime; + + /// Topological predicate + typedef TTopoPredicate TopoPredicate; + + /// Set of points where the distance values are known + typedef typename SetFromImageSelector::Set PointSet; + + /// Khalimsky space + typedef TKSpace KSpace; + + /// Frontier + typedef FrontierPredicate SurfelPredicate; + typedef LightExplicitDigitalSurface Frontier; + /// Surfel + typedef typename Frontier::Surfel Surfel; + typedef typename Frontier::SurfelConstIterator SurfelIterator; + + + /// Partition + typedef PartitionEvolver Partition; + + // ----------------------- Standard services ------------------------------ + public: + + /** + * Constructor. + * @param aK khalimsky space where the digital frontier is defined + * @param aI an image of labels + * @param aD an image of distance values + * @param aS a surfel lying between two regions of @a aI + * @param aF a point functor mapping a speed to points + * @param aP any topological predicate + */ + FrontierEvolver(const KSpace& aK, LImage& aI, DImage& aD, const Surfel& aS, + const Functor& aF, const TopoPredicate& aP, + Partition* aPartitionPtr = NULL); + + /** + * Destructor. Does nothing. + */ + ~FrontierEvolver(); + + /** + * Deform the image of labels during @a aT + * + * @param aT time step + * @return time spent during the deformation + * (equal to aT). + */ + double update(const double& aT); + + + /** + * @return starting surfel of the digital frontier. + */ + Surfel surfel() const; + + /** + * @param aSurfel new starting surfel of the digital frontier. + */ + void setSurfel(const Surfel& aSurfel); + + /** + * @return begin iterator on the surfels of the digital frontier. + */ + SurfelIterator begin() const; + + /** + * @return end iterator on the surfels of the digital frontier. + */ + SurfelIterator end() const; + + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const; + + /** + * Writes/Displays the object on an output stream. + * @param out the output stream where the object is written. + */ + void selfDisplay ( std::ostream & out ) const; + + + // ------------------------- Protected Datas ------------------------------ + protected: + // ------------------------- Private Datas -------------------------------- + private: + + /** + * Constant reference on the khalimsky space + */ + const KSpace& myKSpace; + /** + * Reference on the image of labels + */ + LImage& myLImage; + /** + * Reference on the image of distance values + */ + DImage& myDImage; + /** + * Set of points where the distance values are known + */ + PointSet myPointSet; + /** + * Starting surfel of the digital frontier + */ + Surfel mySurfel; + /** + * Constant reference on the functor + */ + const Functor& myFunctor; + /** + * Constant reference on the topological predicate + */ + const TopoPredicate& myTopoPred; + /** + * Label of the inner region + */ + Label myInnerLabel; + /** + * Label of the outer region + */ + Label myOuterLabel; + /** + * Surfel predicate telling whether a given surfel + * belongs to the frontier or not + */ + SurfelPredicate mySurfelPred; + /** + * (implicit) digital frontier + */ + const Frontier* myFrontier; + /** + * Aliasing pointer on the partition the frontier belongs to + */ + Partition* myPartitionPtr; + + // ------------------------- Hidden services ------------------------------ + protected: + + + /** + * Copy constructor. + * @param other the object to clone. + * Forbidden by default. + */ + FrontierEvolver ( const FrontierEvolver & other ); + + /** + * Assignment. + * @param other the object to copy. + * @return a reference on 'this'. + * Forbidden by default. + */ + FrontierEvolver & operator= ( const FrontierEvolver & other ); + + private: + + + + // ------------------------- Internals ------------------------------------ + private: + + /** + * Return through @a res the points + * of the narrow band + * for which the distance value has been + * computed and stored in @a myDImage + * + * @tparam TOutputIterator a model of output iterator + * + * @param res an output iterator + */ + template + void initNarrowBand(TOutputIterator res); + + /** + * Return through @a res the points + * that can be flipped into the + * adjacent region + * + * @param itb begin iterator on points + * @param ite end iterator on points + * @param aDistanceSpeedIto an output iterator on DistanceSpeed pairs + * @param aCandidateIto an output iterator on candidates + * + * @tparam TInputIterator a model of input iterator on points + * @tparam TOutputIterator1 a model of output iterator on DistanceSpeed pairs + * @tparam TOutputIterator2 a model of output iterator on candidates + * + */ + template + void initCandidates( const TInputIterator& itb, const TInputIterator& ite, + TOutputIterator1 aDistanceSpeedIto, TOutputIterator2 aCandidateIto ); + + + /** + * Checks whether the other frontiers (if any) + * can be modified by the flip of @a aPoint + * @param aPoint any (digital) point + */ + void checkPartition ( const Point& aPoint ); + + /** + * Update @a myLImage for each candidates of the + * range [@a itb , @a ite ) + * + * The points that are not allowed to flip + * (because of the topological predicate) + * are stored through @a ito into a container + * + * @param itb begin iterator on candidates + * @param ite end iterator on candidates + * @param ito output iterator on points + * @param aP (returned) last point + * @param aT (returned) zero-crossing time of the last point + * @param aTMax maximal accepted time (equal to the evolution time step) + * + * @tparam TInputIterator a model of input iterator on candidates + * @tparam TOutputIterator a model of input iterator on points + * + * @return number of flipped points + */ + template + int updateLabelImage( const TInputIterator& itb, const TInputIterator& ite, + TOutputIterator ito, + Point& aP, double &aT, const double& aTMax ); + + /** + * Update @a myDImage at each point of the + * range [@a itb , @a ite ) knowing their + * distance and speed returning by @a aDistanceSpeedIt + * + * The points that belongs to @a aSet are not allowed to flip. + * + * @param itb begin iterator on points + * @param ite end iterator on points + * @param aDistanceSpeedIt an input iterator on DistanceSpeed pairs + * @param aSet any set of points + * @param t evolution time step + * + * @tparam TInputIterator1 a model of input iterator on points + * @tparam TInputIterator2 a model of input iterator on DistanceSpeed pairs + * @tparam TSet STL set like type + * + */ + template + void updateDistanceImage( const TInputIterator1& itb, const TInputIterator1& ite, + TInputIterator2 aDistanceSpeedIt, + const TSet& aSet, const double& t ); + + + /** + * Update the starting surfel @a mySurfel + * of the digital frontier from point @a p + * @param p any (digital) point + */ + void updateFrontier ( const Point& p ); + + /** + * Get inner point. + * @param s a surfel + * @return the inner point + */ + Point getInnerPoint ( const Surfel& s ) const ; + + /** + * Get outer point. + * @param s a surfel + * @return the outer point + */ + Point getOuterPoint ( const Surfel& s ) const ; + + + }; // end of class FrontierEvolver + + + /** + * Overloads 'operator<<' for displaying objects of class 'FrontierEvolver'. + * @param out the output stream where the object is written. + * @param object the object of class 'FrontierEvolver' to write. + * @return the output stream after the writing. + */ + template + std::ostream& + operator<< ( std::ostream & out, + const FrontierEvolver & object ); + + +} // namespace DGtal + + +/////////////////////////////////////////////////////////////////////////////// +// Includes inline functions. +#include "FrontierEvolver.ih" + +// // +/////////////////////////////////////////////////////////////////////////////// + +#endif // !defined FrontierEvolver_h + +#undef FrontierEvolver_RECURSES +#endif // else defined(FrontierEvolver_RECURSES) diff --git a/deformations/FrontierEvolver.ih b/deformations/FrontierEvolver.ih new file mode 100644 index 0000000..7cbe2c1 --- /dev/null +++ b/deformations/FrontierEvolver.ih @@ -0,0 +1,626 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +/** + * @file FrontierEvolver.ih + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * + * @date 2012/03/01 + * + * @brief Implementation of inline methods defined in FrontierEvolver.h + * + * This file is part of the DGtal library. + */ + + +////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// IMPLEMENTATION of inline methods. +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// ----------------------- Standard services ------------------------------ + + +template +inline +DGtal::FrontierEvolver +::FrontierEvolver(const KSpace& aK, LImage& aI, DImage& aD, const Surfel& aS, + const Functor& aF, const TopoPredicate& aP, Partition* aPartitionPtr ) + : myKSpace( aK ), myLImage( aI ), myDImage( aD ), + myPointSet( SetFromImageSelector::get( myDImage ) ), + mySurfel( aS ), myFunctor( aF ), myTopoPred( aP ), + myPartitionPtr( aPartitionPtr), + myInnerLabel( myLImage( getInnerPoint( mySurfel ) ) ), + myOuterLabel( myLImage( getOuterPoint( mySurfel ) ) ), + mySurfelPred( myKSpace, myLImage, myInnerLabel, myOuterLabel ), + myFrontier( new Frontier ( myKSpace, mySurfelPred, + SurfelAdjacency( true ), + mySurfel ) ) +{ + ASSERT( myFrontier ); + + #ifdef WITHINFO + trace.info() << "Labels: " << myInnerLabel << " (inner region)" + << " and " << myOuterLabel << " (outer region) " + << std::endl; + #endif + ASSERT( myInnerLabel != myOuterLabel ); + ASSERT( myKSpace.sIsSurfel( mySurfel ) ); + ASSERT( mySurfelPred( mySurfel ) ); +} + +template +inline +DGtal::FrontierEvolver +::~FrontierEvolver() +{ + delete( myFrontier ); +} + +template +inline +typename DGtal::FrontierEvolver::Surfel +DGtal::FrontierEvolver +::surfel() const +{ + return mySurfel; +} + +template +inline +void +DGtal::FrontierEvolver +::setSurfel(const Surfel& aSurfel) +{ + /// update surfel + mySurfel = aSurfel; + + ASSERT( myKSpace.sIsSurfel( mySurfel ) ); + ASSERT( mySurfelPred( mySurfel ) ); + ASSERT( myInnerLabel == myLImage( getInnerPoint( mySurfel ) ) ); + ASSERT( myOuterLabel == myLImage( getOuterPoint( mySurfel ) ) ); + + /// update frontier + delete ( myFrontier ); + myFrontier = new Frontier ( myKSpace, mySurfelPred, + SurfelAdjacency( true ), + mySurfel ); + ASSERT( myFrontier ); +} + +template +inline +typename DGtal::FrontierEvolver +::SurfelIterator +DGtal::FrontierEvolver +::begin() const +{ + return myFrontier->begin(); +} + +template +inline +typename DGtal::FrontierEvolver +::SurfelIterator +DGtal::FrontierEvolver +::end() const +{ + return myFrontier->end(); +} + +template +inline +bool +DGtal::FrontierEvolver +::isValid() const +{ + return ( ( myInnerLabel == myLImage( getInnerPoint( mySurfel ) ) ) + && ( myOuterLabel == myLImage( getOuterPoint( mySurfel ) ) ) ); +} + + +template +inline +void +DGtal::FrontierEvolver +::selfDisplay ( std::ostream & out ) const +{ + out << "[FrontierEvolver]\n"; + out << "\n"; +} + + +/////////////////////////////////////////////////////////////////////////////// +// ----------------------- Main methods ------------------------------ + + +template +inline +double +DGtal::FrontierEvolver +::update(const double& aT) +{ + ASSERT( myKSpace.sIsSurfel( mySurfel ) ); + ASSERT( mySurfelPred( mySurfel ) ); + ASSERT( myInnerLabel == myLImage( getInnerPoint( mySurfel ) ) ); + ASSERT( myOuterLabel == myLImage( getOuterPoint( mySurfel ) ) ); + + #ifdef WITHINFO + trace.info() << std::endl; + trace.info() << "starting surfel: " << mySurfel << std::endl; + trace.info() << "between: " << myInnerLabel << " (inner region)" + << " and " << myOuterLabel << " (outer region) " + << std::endl; + #endif + + /// narrow band + typedef std::vector Band; + Band narrowBand; + initNarrowBand ( std::back_inserter( narrowBand ) ); + #ifdef WITHINFO + trace.info() << narrowBand.size() << " closest points" << std::endl; + #endif + + /// speed and zero-crossing time computation + typedef std::vector DistanceSpeedVector; + DistanceSpeedVector buffer; + /// candidates to flip + typedef std::vector CandidateVector; + CandidateVector candidates; + initCandidates( narrowBand.begin(), narrowBand.end(), + std::back_inserter( buffer ), + std::back_inserter( candidates ) ); + #ifdef WITHINFO + trace.info() << candidates.size() << " candidates " << std::endl; + #endif + + if (candidates.begin() != candidates.end()) + { //if there are candidates + + /// ordering of the points according + /// to their zero-crossing time + #ifdef WITHINFO + trace.info() << "ordering..." << std::endl; + #endif + + details::CompareSecondElement timeCompare; + std::sort( candidates.begin(), candidates.end(), timeCompare ); + + #ifdef WITHINFO + trace.info() << "Times ranging from " + << candidates.begin()->second + << " to " + << candidates.rbegin()->second + << std::endl; + #endif + + /// flip points one by one, in order, while it is possible + //points not allowed to flip because not simple + std::set forbiddenPoints; + // last point and its zero-crossing time + Point lastP = Point::diagonal(0); + double lastT = 0.0; + int nbFlips = updateLabelImage( candidates.begin(), candidates.end(), + std::inserter( forbiddenPoints, forbiddenPoints.begin() ), + lastP, lastT, aT ); + ASSERT( lastT <= aT ); + #ifdef WITHINFO + trace.info() << nbFlips << " flipped points in " << lastT << " unit time" << std::endl; + #endif + + /// update distance map anyway + #ifdef WITHINFO + trace.info() << "updating signed distance function..." << std::endl; + #endif + updateDistanceImage( narrowBand.begin(), narrowBand.end(), + buffer.begin(), forbiddenPoints, aT ); + + /// update frontier if needed + if (nbFlips > 0) + updateFrontier( lastP ); + + return aT; + } + else + return 0.0; +} + +template +template +inline +void +DGtal::FrontierEvolver +::initNarrowBand( TOutputIterator res ) +{ + + //predicate and FMM definitions + typename Domain::Predicate pointPredicate = myDImage.domain().predicate(); + typedef FMM FMM; + + /// initialization of the band from the + /// points adjacent to the frontier + if (myPointSet.size() == 0) + {//first step + FMM::initFromBelsRange(myKSpace, myFrontier->begin(), myFrontier->end(), + myDImage, myPointSet, 0.5, true); + } + else + {//next steps + + //this renormalization doesn ot work at all + // FMM::initFromBelsRange(myKSpace, myFrontier->begin(), myFrontier->end(), + // myDImage, myDImage, myPointSet, true); + //TODO think about the best way of dealing with adjacentPoints + //and copying it in the band + typedef std::pair PointDistance; + std::map adjacentPoints; + typedef typename std::map::iterator IteratorPointDistance; + + for ( SurfelIterator it = myFrontier->begin(), + itEnd = myFrontier->end(); + it != itEnd; ++it ) + { + Point in( getInnerPoint( *it ) ); + Point out( getOuterPoint( *it ) ); + + ASSERT( myLImage(in) == myInnerLabel ); + Distance din = myDImage( in ); + ASSERT( din <= 0 ); + + ASSERT( myLImage(out) == myOuterLabel ); + Distance dout = myDImage( out ); + ASSERT( dout > 0 ); + + std::pair rin + = adjacentPoints.insert( PointDistance( in, din ) ); + + std::pair rout + = adjacentPoints.insert( PointDistance( out, dout ) ); + } + + myPointSet.clear(); + for ( IteratorPointDistance + it = adjacentPoints.begin(), + itEnd = adjacentPoints.end(); + it != itEnd; ++it ) + { + PointDistance pair( *it ); + insertAndSetValue( myDImage, myPointSet, pair.first, pair.second ); + } + } + + + std::copy( myPointSet.begin(), myPointSet.end(), res ); + + #ifdef WITHINFO + trace.info() << myPointSet.size() << " distinct adjacent points." << std::endl; + #endif + + + /// FMM computation + FMM fmm( myDImage, myPointSet, pointPredicate ); + #ifdef WITHINFO + trace.info() << fmm << std::endl; + #endif + + Point p = Point::diagonal(0); //last point + Distance d = 0; //its distance + //first pass + { + double threshold = std::max( std::abs(fmm.max()),std::abs(fmm.min()) ) + + 2.0; // distance threshold + while ( (fmm.computeOneStep( p, d )) + && (std::abs(d) < threshold) ) + { + ASSERT( myDImage(p) == d ); + #ifdef WITHINFO + if (! ( ((myLImage(p) == myInnerLabel)&&(d<0.0001)) + || ((myLImage(p) == myOuterLabel)&&(d>-0.0001)) + || ( (myLImage(p) != myInnerLabel)&&(myLImage(p) != myOuterLabel) ) ) ) + { + trace.info() << p << " in " << myLImage(p) << " at " << d << std::endl; + } + #endif + + ASSERT( ((myLImage(p) == myInnerLabel)&&(d<0.0001)) + || ((myLImage(p) == myOuterLabel)&&(d>-0.0001)) + || ( (myLImage(p) != myInnerLabel)&&(myLImage(p) != myOuterLabel) ) ); + *res++ = p; + } + } + #ifdef WITHINFO + trace.info() << fmm << std::endl; + #endif + + //second pass + { + double threshold = std::max( std::abs(fmm.max()),std::abs(fmm.min()) ) + + 2.0; // distance threshold + while ( (fmm.computeOneStep( p, d )) + && (std::abs(d) < threshold) ) + { } + } + #ifdef WITHINFO + trace.info() << fmm << std::endl; + #endif + +} + + +template +template +inline +void +DGtal::FrontierEvolver +::initCandidates( const TInputIterator& itb, const TInputIterator& ite, + TOutputIterator1 aDistanceSpeedIto, TOutputIterator2 aCandidateIto ) +{ + for ( TInputIterator it = itb; + it != ite; ++it) + { + // point + Point p = *it; + // distance + Distance d = myDImage( p ); + // speed + Speed v = myFunctor( p ); + // storing distance and speed + *aDistanceSpeedIto++ = DistanceSpeed( d, v ); + // new candidate with its zero-crossing time + if ( ( myLImage( p ) == myInnerLabel ) + || (myLImage( p ) == myOuterLabel ) ) + { + if ( ( (d>0) && (v<0) ) + || ( (d<=0) && (v>0) ) ) + { // if opposite signs (and v != 0) + double t = - static_cast( d ) / v; + ASSERT( t >= 0 ); + // storing candidate + *aCandidateIto++ = PointTime( p, t ); + } + } + } +} + + +template +template +inline +int +DGtal::FrontierEvolver +::updateLabelImage( const TInputIterator& itb, const TInputIterator& ite, TOutputIterator ito, + Point& aP, double& aT, const double& aTMax ) +{ + + unsigned int nbFlips = 0; + + for (TInputIterator it = itb; + ( (it != ite)&&(it->second <= aTMax) ); ++it) + { + aP = it->first; + aT = it->second; + const Label label = myLImage( aP ); + const Label oppositeLabel = + (label == myInnerLabel)?myOuterLabel:myInnerLabel; + + if ( myTopoPred( aP, oppositeLabel ) ) + { /// flip + checkPartition( aP ); + #ifdef WITHINFO + trace.emphase() << aP << " flipped into region " + << oppositeLabel << std::endl; + #endif + myLImage.setValue( aP, oppositeLabel ); + ++nbFlips; + } + else + { + *ito++ = aP; + #ifdef WITHINFO + trace.emphase() << aP << " is not a simple point!" << std::endl; + #endif + } + } + + return nbFlips; +} + +template +inline +void +DGtal::FrontierEvolver +::updateFrontier(const Point& p) +{ + /// spel creation + typename KSpace::SCell spel; + const Label pLabel = myLImage(p); + if ( pLabel == myInnerLabel ) + { + ASSERT( (myDImage(p) <= 0) ); + spel = myKSpace.sSpel( p, KSpace::POS ); + } + else if ( pLabel == myOuterLabel ) + { + ASSERT( (myDImage(p) > 0) ); + spel = myKSpace.sSpel( p, KSpace::NEG ); + } + else + ASSERT( false && "impossible label in updateFrontier method" ); + + /// for each direction + Surfel newSurfel; + bool flag = false; + for ( typename KSpace::DirIterator q = myKSpace.sDirs( spel ); + ( (q != 0)&&(!flag) ); ++q ) + { + const DGtal::Dimension dir = *q; + + /// for the direct orientation + typename KSpace::SCell surfel + = myKSpace.sDirectIncident( spel, dir ); + ASSERT( myKSpace.sIsSurfel( surfel ) ); + if ( mySurfelPred( surfel ) ) + { + newSurfel = surfel; + flag = true; + } + if (!flag) + { + surfel = myKSpace.sIndirectIncident( spel, dir ); + ASSERT( myKSpace.sIsSurfel( surfel ) ); + if ( mySurfelPred( surfel ) ) + { + newSurfel = surfel; + flag = true; + } + } + } + ASSERT( flag && "last flipped point must be a border point in updateFrontier method" ); + + this->setSurfel( newSurfel ); + +} + + +template +template +inline +void +DGtal::FrontierEvolver +::updateDistanceImage( const TInputIterator1& itb, const TInputIterator1& ite, + TInputIterator2 aDistanceSpeedIt, + const TSet& aSet, const double& t ) +{ + for (TInputIterator1 it = itb; + it != ite; ++it, ++aDistanceSpeedIt) + { + // point + const Point p = *it; + // distance and speed + const DistanceSpeed pair( *aDistanceSpeedIt ); + Distance newDist = pair.first + t * pair.second; + + //label + const Label pLabel = myLImage( p ); + + const Distance eps = std::numeric_limits::epsilon(); + // correction due to the approximation error + if ( ( (newDist < 0.0001)&&(newDist > -0.0001) ) + //correction due to the non simplicity + || ( aSet.find( p ) != aSet.end() ) ) + { + if (pLabel == myInnerLabel) newDist = -eps; + else newDist = eps; + } + + #ifdef WITHINFO + if ( !( ( (newDist <= 0)&&(myLImage( p ) == myInnerLabel) ) + || ( (newDist > 0)&&(myLImage( p ) == myOuterLabel) ) + || ( (myLImage( p ) != myInnerLabel)&&(myLImage( p ) != myOuterLabel) ) ) ) + { + trace.info() << p << " in " << myLImage( p ) + << " at " << newDist << std::endl; + } + #endif + + ASSERT( ( (newDist <= 0)&&(myLImage( p ) == myInnerLabel) ) + || ( (newDist > 0)&&(myLImage( p ) == myOuterLabel) ) + || ( (myLImage( p ) != myInnerLabel)&&(myLImage( p ) != myOuterLabel) ) ); + + + // set value + myDImage.setValue( p, newDist ); + + } + +} + + +/////////////////////////////////////////////////////////////////////////////// +// ----------------------- Small Helpers ------------------------------ +template +inline +void +DGtal::FrontierEvolver +::checkPartition(const Point& aPoint) +{ + if (myPartitionPtr != NULL) + { + myPartitionPtr->checkFrontiers( this, aPoint ); + } +} + + +template +inline +typename DGtal::FrontierEvolver::Point +DGtal::FrontierEvolver +::getInnerPoint(const Surfel& s) const +{ + return myKSpace.sCoords( myKSpace.sDirectIncident( s, *myKSpace.sOrthDirs( s ) ) ); +} + +template +inline +typename DGtal::FrontierEvolver::Point +DGtal::FrontierEvolver +::getOuterPoint(const Surfel& s) const +{ + return myKSpace.sCoords( myKSpace.sIndirectIncident( s, *myKSpace.sOrthDirs( s ) ) ); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Implementation of inline functions // + +template +inline +std::ostream& +DGtal::operator<< ( std::ostream & out, + const FrontierEvolver & object ) +{ + object.selfDisplay( out ); + return out; +} + +// // +/////////////////////////////////////////////////////////////////////////////// + + diff --git a/deformations/FrontierEvolver2.h b/deformations/FrontierEvolver2.h new file mode 100644 index 0000000..45e3285 --- /dev/null +++ b/deformations/FrontierEvolver2.h @@ -0,0 +1,485 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +#pragma once + +/** + * @file FrontierEvolver2.h + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * + * @date 2012/03/01 + * + * Header file for module FrontierEvolver2.cpp + * + * This file is part of the DGtal library. + */ + +#if defined(FrontierEvolver2_RECURSES) +#error Recursive header files inclusion detected in FrontierEvolver2.h +#else // defined(FrontierEvolver2_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define FrontierEvolver2_RECURSES + +#if !defined FrontierEvolver2_h +/** Prevents repeated inclusion of headers. */ +#define FrontierEvolver2_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include + +#include "DGtal/base/Common.h" +#include "DGtal/images/CImage.h" +#include "DGtal/kernel/CPointFunctor.h" +#include "DGtal/kernel/CPointPredicate.h" + +//predicates +#include "DGtal/base/BasicBoolFunctors.h" +#include "DGtal/kernel/BasicPointPredicates.h" +#include "PointPredicates.h" + + + +// set +#include "DGtal/images/ImageContainerBySTLMap.h" +#include "DGtal/kernel/sets/DigitalSetFromMap.h" +#include "DGtal/kernel/sets/DigitalSetBySTLSet.h" +#include "DGtal/kernel/sets/DigitalSetInserter.h" +#include "DGtal/images/ImageHelper.h" + +// FMM +#include "FMM.h" + +// frontier +#include "DGtal/topology/SurfelAdjacency.h" +#include "DGtal/topology/helpers/FrontierPredicate.h" +#include "DGtal/topology/LightExplicitDigitalSurface.h" + +//partition +#include "PartitionEvolver.h" +#include "FrontierEvolverHelpers.h" +////////////////////////////////////////////////////////////////////////////// + +namespace DGtal +{ + + + ///////////////////////////////////////////////////////////////////////////// + // template class FrontierEvolver2 + /** + * Description of template class 'FrontierEvolver2'

+ * \brief Aim: This class is a way of deforming an image of labels + * around a digital frontier (ie. connected contact surface) + * between two adjacent regions according to a speed field. + * The speed field is only computed around the interface + * (this task is delegated to a point functor) and is then + * extended outward the interface in the normal direction. + * + * At each step, a signed distance function is updated and extended + * within a narrow band of width 4 (2 on each side) + * from the values known at the points adjacent to the digital frontier. + * The points lying in the narrow band are sorted according to + * their time of zero-crossing (ie. their distance to the interface + * divided by their speed) so that they are flipped from one region + * to another, one by one and in order, until a time greater than + * a given threshold is reached, but only if a given predicate + * based on topological properties returns true. + * + * @tparam TKSpace a model of CCellularGridSpaceND + * @tparam TLabelImage a model of CImage (storing labels) + * @tparam TDistanceImage a model of CImage (storing distance values) + * @tparam TFunctor a model of CPointFunctor + * @tparam TTopoPredicate a model of topological predicate + */ + template + class FrontierEvolver2 + { + + + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + BOOST_STATIC_ASSERT + (( ConceptUtils::SameType< typename TKSpace::Point, + typename TLabelImage::Point>::value )); + BOOST_STATIC_ASSERT + (( ConceptUtils::SameType< typename TKSpace::Point, + typename TDistanceImage::Point>::value )); + + BOOST_CONCEPT_ASSERT(( concepts::CPointFunctor )); + BOOST_STATIC_ASSERT + (( ConceptUtils::SameType< typename TKSpace::Point, + typename TFunctor::Point>::value )); + + // BOOST_CONCEPT_ASSERT(( concepts::CPointPredicate )); + // BOOST_STATIC_ASSERT + // (( ConceptUtils::SameType< typename TKSpace::Point, + // typename TTopoPredicate::Point>::value )); + //TODO testing TTopoPredicate as a binary predicate on points and labels + + // ----------------------- Types ------------------------------ + public: + + /// Image of labels + typedef TLabelImage LImage; + typedef typename LImage::Value Label; + typedef typename LImage::Domain Domain; + typedef typename Domain::Point Point; + + /// Image of distance values + typedef TDistanceImage DImage; + typedef typename DImage::Value Distance; + + /// Point functor for the mapping points-speed + typedef TFunctor Functor; + typedef typename Functor::Value Speed; + typedef std::pair DistanceSpeed; + typedef std::pair PointTime; + + /// Topological predicate + typedef TTopoPredicate TopoPredicate; + + /// Set of points where the distance values are known + typedef typename SetFromImageSelector::Set PointSet; + + /// Khalimsky space + typedef TKSpace KSpace; + + /// Frontier + typedef FrontierPredicate SurfelPredicate; + typedef LightExplicitDigitalSurface Frontier; + /// Surfel + typedef typename Frontier::Surfel Surfel; + typedef typename Frontier::SurfelConstIterator SurfelIterator; + + + /// Partition + typedef PartitionEvolver Partition; + + // ----------------------- Standard services ------------------------------ + public: + + /** + * Constructor. + * @param aK khalimsky space where the digital frontier is defined + * @param aI an image of labels + * @param aD an image of distance values + * @param aS a surfel lying between two regions of @a aI + * @param aF a point functor mapping a speed to points + * @param aP any topological predicate + */ + FrontierEvolver2(const KSpace& aK, LImage& aI, DImage& aD, const Surfel& aS, + const Functor& aF, const TopoPredicate& aP, + Partition* aPartitionPtr = NULL); + + /** + * Destructor. Does nothing. + */ + ~FrontierEvolver2(); + + /** + * Deform the image of labels during @a aT + * + * @param aT time step + * @return time spent during the deformation + * (equal to aT). + */ + double update(const double& aT); + + + /** + * @return starting surfel of the digital frontier. + */ + Surfel surfel() const; + + /** + * @param aSurfel new starting surfel of the digital frontier. + */ + void setSurfel(const Surfel& aSurfel); + + /** + * @return begin iterator on the surfels of the digital frontier. + */ + SurfelIterator begin() const; + + /** + * @return end iterator on the surfels of the digital frontier. + */ + SurfelIterator end() const; + + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const; + + /** + * Writes/Displays the object on an output stream. + * @param out the output stream where the object is written. + */ + void selfDisplay ( std::ostream & out ) const; + + + // ------------------------- Protected Datas ------------------------------ + protected: + // ------------------------- Private Datas -------------------------------- + private: + + /** + * Constant reference on the khalimsky space + */ + const KSpace& myKSpace; + /** + * Reference on the image of labels + */ + LImage& myLImage; + /** + * Reference on the image of distance values + */ + DImage& myDImage; + /** + * Set of points where the distance values are known + */ + PointSet myPointSet; + /** + * Starting surfel of the digital frontier + */ + Surfel mySurfel; + /** + * Constant reference on the functor + */ + const Functor& myFunctor; + /** + * Constant reference on the topological predicate + */ + const TopoPredicate& myTopoPred; + /** + * Label of the inner region + */ + Label myInnerLabel; + /** + * Label of the outer region + */ + Label myOuterLabel; + /** + * Surfel predicate telling whether a given surfel + * belongs to the frontier or not + */ + SurfelPredicate mySurfelPred; + /** + * (implicit) digital frontier + */ + const Frontier* myFrontier; + /** + * Aliasing pointer on the partition the frontier belongs to + */ + Partition* myPartitionPtr; + + // ------------------------- Hidden services ------------------------------ + protected: + + + /** + * Copy constructor. + * @param other the object to clone. + * Forbidden by default. + */ + FrontierEvolver2 ( const FrontierEvolver2 & other ); + + /** + * Assignment. + * @param other the object to copy. + * @return a reference on 'this'. + * Forbidden by default. + */ + FrontierEvolver2 & operator= ( const FrontierEvolver2 & other ); + + private: + + + + // ------------------------- Internals ------------------------------------ + private: + + /** + * Return through @a res the points + * of the narrow band + * for which the distance value has been + * computed and stored in @a myDImage + * + * @tparam TOutputIterator a model of output iterator + * + * @param res an output iterator + */ + template + void initNarrowBand(TOutputIterator res); + + /** + * Return through @a res the points + * that can be flipped into the + * adjacent region + * + * @param itb begin iterator on points + * @param ite end iterator on points + * @param aDistanceSpeedIto an output iterator on DistanceSpeed pairs + * @param aCandidateIto an output iterator on candidates + * + * @tparam TInputIterator a model of input iterator on points + * @tparam TOutputIterator1 a model of output iterator on DistanceSpeed pairs + * @tparam TOutputIterator2 a model of output iterator on candidates + * + */ + template + void initCandidates( const TInputIterator& itb, const TInputIterator& ite, + TOutputIterator1 aDistanceSpeedIto, TOutputIterator2 aCandidateIto ); + + + /** + * Checks whether the other frontiers (if any) + * can be modified by the flip of @a aPoint + * @param aPoint any (digital) point + */ + void checkPartition ( const Point& aPoint ); + + /** + * Update @a myLImage for each candidates of the + * range [@a itb , @a ite ) + * + * The points that are not allowed to flip + * (because of the topological predicate) + * are stored through @a ito into a container + * + * @param itb begin iterator on candidates + * @param ite end iterator on candidates + * @param ito output iterator on points + * @param aP (returned) last point + * @param aT (returned) zero-crossing time of the last point + * @param aTMax maximal accepted time (equal to the evolution time step) + * + * @tparam TInputIterator a model of input iterator on candidates + * @tparam TOutputIterator a model of input iterator on points + * + * @return number of flipped points + */ + template + int updateLabelImage( const TInputIterator& itb, const TInputIterator& ite, + TOutputIterator ito, + Point& aP, double &aT, const double& aTMax ); + + /** + * Update @a myDImage at each point of the + * range [@a itb , @a ite ) knowing their + * distance and speed returning by @a aDistanceSpeedIt + * + * The points that belongs to @a aSet are not allowed to flip. + * + * @param itb begin iterator on points + * @param ite end iterator on points + * @param aDistanceSpeedIt an input iterator on DistanceSpeed pairs + * @param aSet any set of points + * @param t evolution time step + * + * @tparam TInputIterator1 a model of input iterator on points + * @tparam TInputIterator2 a model of input iterator on DistanceSpeed pairs + * @tparam TSet STL set like type + * + */ + template + void updateDistanceImage( const TInputIterator1& itb, const TInputIterator1& ite, + TInputIterator2 aDistanceSpeedIt, + const TSet& aSet, const double& t ); + + public: + + /** + * Update the starting surfel @a mySurfel + * of the digital frontier from point @a p + * before the flip. The frontier is tracked + * until a surfel that is not adjacent to @a p + * is found. + * + * @param p any (digital) point + * @return 'true' if a new surfel has been found + * 'false' otherwise + */ + bool updateFrontier ( const Point& p ); + + private: + + /** + * Update the starting surfel @a mySurfel + * of the digital frontier from point @a p + * after the flip. Among the surfels adjacent + * to @a p one surfel is chosen. + * + * @param p any (digital) point + * @return 'true' if a new surfel has been found + * 'false' otherwise + */ + bool retrieveFrontier ( const Point& p ); + + + /** + * Get inner point. + * @param s a surfel + * @return the inner point + */ + Point getInnerPoint ( const Surfel& s ) const ; + + /** + * Get outer point. + * @param s a surfel + * @return the outer point + */ + Point getOuterPoint ( const Surfel& s ) const ; + + + }; // end of class FrontierEvolver2 + + + /** + * Overloads 'operator<<' for displaying objects of class 'FrontierEvolver2'. + * @param out the output stream where the object is written. + * @param object the object of class 'FrontierEvolver2' to write. + * @return the output stream after the writing. + */ + template + std::ostream& + operator<< ( std::ostream & out, + const FrontierEvolver2 & object ); + + +} // namespace DGtal + + +/////////////////////////////////////////////////////////////////////////////// +// Includes inline functions. +#include "FrontierEvolver2.ih" + +// // +/////////////////////////////////////////////////////////////////////////////// + +#endif // !defined FrontierEvolver2_h + +#undef FrontierEvolver2_RECURSES +#endif // else defined(FrontierEvolver2_RECURSES) diff --git a/deformations/FrontierEvolver2.ih b/deformations/FrontierEvolver2.ih new file mode 100644 index 0000000..1f29120 --- /dev/null +++ b/deformations/FrontierEvolver2.ih @@ -0,0 +1,704 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +/** + * @file FrontierEvolver2.ih + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * + * @date 2012/03/01 + * + * @brief Implementation of inline methods defined in FrontierEvolver2.h + * + * This file is part of the DGtal library. + */ + + +////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// IMPLEMENTATION of inline methods. +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// ----------------------- Standard services ------------------------------ + + +template +inline +DGtal::FrontierEvolver2 +::FrontierEvolver2(const KSpace& aK, LImage& aI, DImage& aD, const Surfel& aS, + const Functor& aF, const TopoPredicate& aP, Partition* aPartitionPtr ) + : myKSpace( aK ), myLImage( aI ), myDImage( aD ), + myPointSet( SetFromImageSelector::get( myDImage ) ), + mySurfel( aS ), myFunctor( aF ), myTopoPred( aP ), + myPartitionPtr( aPartitionPtr), + myInnerLabel( myLImage( getInnerPoint( mySurfel ) ) ), + myOuterLabel( myLImage( getOuterPoint( mySurfel ) ) ), + mySurfelPred( myKSpace, myLImage, myInnerLabel, myOuterLabel ), + myFrontier( new Frontier ( myKSpace, mySurfelPred, + SurfelAdjacency( true ), + mySurfel ) ) +{ + ASSERT( myFrontier ); + + #ifdef WITHINFO + trace.info() << "Labels: " << myInnerLabel << " (inner region)" + << " and " << myOuterLabel << " (outer region) " + << std::endl; + #endif + ASSERT( myInnerLabel != myOuterLabel ); + ASSERT( myKSpace.sIsSurfel( mySurfel ) ); + ASSERT( mySurfelPred( mySurfel ) ); +} + +template +inline +DGtal::FrontierEvolver2 +::~FrontierEvolver2() +{ + delete( myFrontier ); +} + +template +inline +typename DGtal::FrontierEvolver2::Surfel +DGtal::FrontierEvolver2 +::surfel() const +{ + return mySurfel; +} + +template +inline +void +DGtal::FrontierEvolver2 +::setSurfel(const Surfel& aSurfel) +{ + /// update surfel + mySurfel = aSurfel; + + ASSERT( myKSpace.sIsSurfel( mySurfel ) ); + ASSERT( mySurfelPred( mySurfel ) ); + ASSERT( myInnerLabel == myLImage( getInnerPoint( mySurfel ) ) ); + ASSERT( myOuterLabel == myLImage( getOuterPoint( mySurfel ) ) ); + + /// update frontier + delete ( myFrontier ); + myFrontier = new Frontier ( myKSpace, mySurfelPred, + SurfelAdjacency( true ), + mySurfel ); + ASSERT( myFrontier ); +} + +template +inline +typename DGtal::FrontierEvolver2 +::SurfelIterator +DGtal::FrontierEvolver2 +::begin() const +{ + return myFrontier->begin(); +} + +template +inline +typename DGtal::FrontierEvolver2 +::SurfelIterator +DGtal::FrontierEvolver2 +::end() const +{ + return myFrontier->end(); +} + +template +inline +bool +DGtal::FrontierEvolver2 +::isValid() const +{ + return ( ( myInnerLabel == myLImage( getInnerPoint( mySurfel ) ) ) + && ( myOuterLabel == myLImage( getOuterPoint( mySurfel ) ) ) ); +} + + +template +inline +void +DGtal::FrontierEvolver2 +::selfDisplay ( std::ostream & out ) const +{ + out << "[FrontierEvolver2]\n"; + out << "\n"; +} + + +/////////////////////////////////////////////////////////////////////////////// +// ----------------------- Main methods ------------------------------ + + +template +inline +double +DGtal::FrontierEvolver2 +::update(const double& aT) +{ + ASSERT( myKSpace.sIsSurfel( mySurfel ) ); + ASSERT( mySurfelPred( mySurfel ) ); + ASSERT( myInnerLabel == myLImage( getInnerPoint( mySurfel ) ) ); + ASSERT( myOuterLabel == myLImage( getOuterPoint( mySurfel ) ) ); + + #ifdef WITHINFO + trace.info() << std::endl; + trace.info() << "starting surfel: " << mySurfel << std::endl; + trace.info() << "between: " << myInnerLabel << " (inner region)" + << " and " << myOuterLabel << " (outer region) " + << std::endl; + #endif + + /// narrow band + typedef std::vector Band; + Band narrowBand; + initNarrowBand ( std::back_inserter( narrowBand ) ); + #ifdef WITHINFO + trace.info() << narrowBand.size() << " closest points" << std::endl; + #endif + + /// speed and zero-crossing time computation + typedef std::vector DistanceSpeedVector; + DistanceSpeedVector buffer; + /// candidates to flip + typedef std::vector CandidateVector; + CandidateVector candidates; + initCandidates( narrowBand.begin(), narrowBand.end(), + std::back_inserter( buffer ), + std::back_inserter( candidates ) ); + ASSERT( narrowBand.size() == buffer.size() ); + ASSERT( narrowBand.size() >= candidates.size() ); + #ifdef WITHINFO + trace.info() << candidates.size() << " candidates " << std::endl; + #endif + + if ( (narrowBand.size() != 0)&&(candidates.size() != 0) ) + { //if there are candidates + + /// ordering of the points according + /// to their zero-crossing time + #ifdef WITHINFO + trace.info() << "ordering..." << std::endl; + #endif + + details::CompareSecondElement timeCompare; + std::sort( candidates.begin(), candidates.end(), timeCompare ); + + #ifdef WITHINFO + trace.info() << "Times ranging from " + << candidates.begin()->second + << " to " + << candidates.rbegin()->second + << std::endl; + #endif + + /// flip points one by one, in order, while it is possible + //points not allowed to flip because not simple + std::set forbiddenPoints; + // last point and its zero-crossing time + Point lastP = Point::diagonal(0); + double lastT = 0.0; + int nbFlips = updateLabelImage( candidates.begin(), candidates.end(), + std::inserter( forbiddenPoints, forbiddenPoints.begin() ), + lastP, lastT, aT ); + ASSERT( lastT <= aT ); + #ifdef WITHINFO + trace.info() << nbFlips << " flipped points in " << lastT << " unit time" << std::endl; + #endif + + /// update distance map anyway + #ifdef WITHINFO + trace.info() << "updating signed distance function..." << std::endl; + #endif + updateDistanceImage( narrowBand.begin(), narrowBand.end(), + buffer.begin(), forbiddenPoints, aT ); + + return aT; + } + else + return 0.0; +} + +template +template +inline +void +DGtal::FrontierEvolver2 +::initNarrowBand( TOutputIterator res ) +{ + + //predicate and FMM definitions + typedef TwoLabelsPredicate > LabelPredicate; + typedef CascadingPointPredicate< typename Domain::Predicate, + LabelPredicate > PointPredicate4FMM; + LabelPredicate predOnLabels( myLImage, myInnerLabel, myOuterLabel ); + PointPredicate4FMM pointPredicate( myLImage.domain().predicate(), predOnLabels ); + typedef FMM FMM; + // typename Domain::Predicate pointPredicate = myDImage.domain().predicate(); + // typedef FMM FMM; + + /// initialization of the band from the + /// points adjacent to the digital frontier + if (myPointSet.size() == 0) + {//first step + FMM::initFromBelsRange(myKSpace, myFrontier->begin(), myFrontier->end(), + myDImage, myPointSet, 0.5, true); + } + else + {//next steps + + //TODO think about the best way of dealing with adjacentPoints + //and copying it in the band + typedef std::pair PointDistance; + std::map adjacentPoints; + typedef typename std::map::iterator IteratorPointDistance; + + for ( SurfelIterator it = myFrontier->begin(), + itEnd = myFrontier->end(); + it != itEnd; ++it ) + { + Point in( getInnerPoint( *it ) ); + ASSERT( myLImage(in) == myInnerLabel ); + Distance din = myDImage( in ); + //arbitrary correction + if (din > 0) din = -0.5; + ASSERT( din <= 0 ); + std::pair rin + = adjacentPoints.insert( PointDistance( in, din ) ); + + Point out( getOuterPoint( *it ) ); + ASSERT( myLImage(out) == myOuterLabel ); + Distance dout = myDImage( out ); + //arbitrary correction + if (dout <= 0) dout = 0.5; + ASSERT( dout > 0 ); + std::pair rout + = adjacentPoints.insert( PointDistance( out, dout ) ); + } + + myPointSet.clear(); + for ( IteratorPointDistance + it = adjacentPoints.begin(), + itEnd = adjacentPoints.end(); + it != itEnd; ++it ) + { + PointDistance pair( *it ); + insertAndSetValue( myDImage, myPointSet, pair.first, pair.second ); + } + } + + #ifdef WITHINFO + trace.info() << myPointSet.size() << " distinct adjacent points." << std::endl; + if (myPointSet.size() <= 4) +{ + std::copy( myPointSet.begin(), myPointSet.end(), ostream_iterator(std::cerr, ", ") ); +} + #endif + + + /// FMM computation + FMM fmm( myDImage, myPointSet, pointPredicate, + myDImage.domain().size(), 3.0 ); + #ifdef WITHINFO + trace.info() << fmm << std::endl; + #endif + + fmm.compute(); + + #ifdef WITHINFO + trace.info() << fmm << std::endl; + #endif + + std::copy( myPointSet.begin(), myPointSet.end(), res ); +} + + +template +template +inline +void +DGtal::FrontierEvolver2 +::initCandidates( const TInputIterator& itb, const TInputIterator& ite, + TOutputIterator1 aDistanceSpeedIto, TOutputIterator2 aCandidateIto ) +{ + + /// computing speed + typedef ImageContainerBySTLMap SpeedImage; + SpeedImage speedImage( myDImage.domain(), 0.0 ); + + for ( SurfelIterator it = myFrontier->begin(), + itEnd = myFrontier->end(); + it != itEnd; ++it ) + { + Point in( getInnerPoint( *it ) ); + ASSERT( myLImage(in) == myInnerLabel ); + speedImage.setValue( in, myFunctor( in ) ); + + Point out( getOuterPoint( *it ) ); + ASSERT( myLImage(out) == myOuterLabel ); + speedImage.setValue( out, myFunctor( out ) ); + } + #ifdef WITHINFO + trace.info() << " speed of the adjacent points... " << std::endl; + #endif + + + {/// extension speed + typedef ImageContainerBySTLMap TmpDImage; + TmpDImage tmpDImage( myDImage.domain(), 0.0 ); + typedef DigitalSetFromMap< ImageContainerBySTLMap > TmpSet; + TmpSet tmpSet( tmpDImage ); + + SpeedExtrapolator + speedFunctor( tmpDImage, tmpSet, speedImage ); + + typedef TwoLabelsPredicate > LabelPredicate; + typedef CascadingPointPredicate< typename Domain::Predicate, + LabelPredicate > PointPredicate4FMM; + LabelPredicate predOnLabels( myLImage, myInnerLabel, myOuterLabel ); + PointPredicate4FMM pointPredicate( myLImage.domain().predicate(), predOnLabels ); + typedef FMM FMM; + FMM::initFromBelsRange( myKSpace, + myFrontier->begin(), myFrontier->end(), + myDImage, tmpDImage, tmpSet ); + + //TODO point predicate must be derived from the narrow band + FMM fmm( tmpDImage, tmpSet, pointPredicate, + myDImage.domain().size(), 3.0 ); + Point lastPt = Point::diagonal(0); //last point + double lastDist = 0.0; //its distance + while ( fmm.computeOneStep( lastPt, lastDist ) ) + { //new speed value + speedImage.setValue( lastPt, speedFunctor( lastPt ) ); + } + #ifdef WITHINFO + trace.info() << " extension... " << std::endl; + #endif + } + + /// candidates + for ( TInputIterator it = itb; + it != ite; ++it) + { + // point + Point p = *it; + // distance + Distance d = myDImage( p ); + // speed + Speed v = speedImage( p ); + // storing distance and speed + *aDistanceSpeedIto++ = DistanceSpeed( d, v ); + // new candidate with its zero-crossing time + if ( ( myLImage( p ) == myInnerLabel ) + || (myLImage( p ) == myOuterLabel ) ) + { + if ( ( (d>0) && (v<0) ) + || ( (d<=0) && (v>0) ) ) + { // if opposite signs (and v != 0) + double t = - static_cast( d ) / v; + ASSERT( t >= 0 ); + // storing candidate + *aCandidateIto++ = PointTime( p, t ); + } + } + } +} + + +template +template +inline +int +DGtal::FrontierEvolver2 +::updateLabelImage( const TInputIterator& itb, const TInputIterator& ite, TOutputIterator ito, + Point& aP, double& aT, const double& aTMax ) +{ + + unsigned int nbFlips = 0; + + for (TInputIterator it = itb; + ( (it != ite)&&(it->second <= aTMax) ); ++it) + { + aP = it->first; + aT = it->second; + const Label label = myLImage( aP ); + const Label oppositeLabel = + (label == myInnerLabel)?myOuterLabel:myInnerLabel; + + if ( myTopoPred( aP, oppositeLabel ) ) + { /// flip + checkPartition( aP ); + bool flagBefore = updateFrontier( aP ); + myLImage.setValue( aP, oppositeLabel ); + #ifdef WITHINFO + trace.emphase() << aP << " flipped into region " + << oppositeLabel << std::endl; + #endif + if (!flagBefore) + { + bool flagAfter = retrieveFrontier( aP ); + ASSERT( flagAfter && + "flipped point must be a border point in updateFrontierAfter method" ); + } + ++nbFlips; + } + else + { + *ito++ = aP; + #ifdef WITHINFO + trace.emphase() << aP << " is not a simple point!" << std::endl; + #endif + } + } + + return nbFlips; +} + +template +inline +bool +DGtal::FrontierEvolver2 +::retrieveFrontier(const Point& p) +{ + +#ifdef WITHINFO + trace.info() << "updating frontier..." << std::endl; +#endif + + /// spel creation + typename KSpace::SCell spel; + const Label pLabel = myLImage(p); + if ( pLabel == myInnerLabel ) + spel = myKSpace.sSpel( p, KSpace::POS ); + else + spel = myKSpace.sSpel( p, KSpace::NEG ); + + /// for each direction + Surfel newSurfel; + bool flag = false; + for ( typename KSpace::DirIterator q = myKSpace.sDirs( spel ); + ( (q != 0)&&(!flag) ); ++q ) + { + const DGtal::Dimension dir = *q; + + /// for the direct orientation + typename KSpace::SCell surfel + = myKSpace.sDirectIncident( spel, dir ); + ASSERT( myKSpace.sIsSurfel( surfel ) ); + if ( mySurfelPred( surfel ) ) + { + newSurfel = surfel; + flag = true; + } + if (!flag) + { + surfel = myKSpace.sIndirectIncident( spel, dir ); + ASSERT( myKSpace.sIsSurfel( surfel ) ); + if ( mySurfelPred( surfel ) ) + { + newSurfel = surfel; + flag = true; + } + } + } + if (flag) + { + this->setSurfel( newSurfel ); + return true; + } + else + return false; +} + +template +inline +bool +DGtal::FrontierEvolver2 +::updateFrontier(const Point& p) +{ + + const Surfel s = mySurfel; + if ( (getInnerPoint(s) == p)||(getOuterPoint(s) == p) ) + { /// if s is adjacent to p + + #ifdef WITHINFO + trace.info() << "updating frontier..." << std::endl; + #endif + /// tracking until a new surfel is found + bool flag = true; + SurfelIterator it = myFrontier->begin(); + SurfelIterator itEnd = myFrontier->end(); + SCell newS; + while ( (it != itEnd)&&(flag) ) + { + newS = *it; + if ( (getInnerPoint(newS) != p) && (getOuterPoint(newS) != p) ) + {/// if newS is not adjacent to aPoint + flag = false; + } + ++it; + } + if (!flag) + { + setSurfel( newS ); + #ifdef WITHINFO + trace.info() << s << " moved to " << newS << std::endl; + #endif + return true; + } + else + return false; + } + else + return true; + +} + + +template +template +inline +void +DGtal::FrontierEvolver2 +::updateDistanceImage( const TInputIterator1& itb, const TInputIterator1& ite, + TInputIterator2 aDistanceSpeedIt, + const TSet& aSet, const double& t ) +{ + for (TInputIterator1 it = itb; + it != ite; ++it, ++aDistanceSpeedIt) + { + // point + const Point p = *it; + // distance and speed + const DistanceSpeed pair( *aDistanceSpeedIt ); + Distance newDist = pair.first + t * pair.second; + + //label + const Label pLabel = myLImage( p ); + + const Distance eps = std::numeric_limits::epsilon(); + // correction due to the approximation error + if ( ( (newDist < 0.0001)&&(newDist > -0.0001) ) + //correction due to the non simplicity + || ( aSet.find( p ) != aSet.end() ) ) + { + if (pLabel == myInnerLabel) newDist = -eps; + else newDist = eps; + } + + #ifdef WITHINFO + if ( !( ( (newDist <= 0)&&(myLImage( p ) == myInnerLabel) ) + || ( (newDist > 0)&&(myLImage( p ) == myOuterLabel) ) + || ( (myLImage( p ) != myInnerLabel)&&(myLImage( p ) != myOuterLabel) ) ) ) + { + trace.info() << p << " in " << myLImage( p ) + << " at " << newDist << std::endl; + } + #endif + + // ASSERT( ( (newDist <= 0)&&(myLImage( p ) == myInnerLabel) ) + // || ( (newDist > 0)&&(myLImage( p ) == myOuterLabel) ) + // || ( (myLImage( p ) != myInnerLabel)&&(myLImage( p ) != myOuterLabel) ) ); + + + // set value + myDImage.setValue( p, newDist ); + + } + +} + + +/////////////////////////////////////////////////////////////////////////////// +// ----------------------- Small Helpers ------------------------------ +template +inline +void +DGtal::FrontierEvolver2 +::checkPartition(const Point& aPoint) +{ + if (myPartitionPtr != NULL) + { + myPartitionPtr->checkFrontiers( this, aPoint ); + } +} + + +template +inline +typename DGtal::FrontierEvolver2::Point +DGtal::FrontierEvolver2 +::getInnerPoint(const Surfel& s) const +{ + return myKSpace.sCoords( myKSpace.sDirectIncident( s, *myKSpace.sOrthDirs( s ) ) ); +} + +template +inline +typename DGtal::FrontierEvolver2::Point +DGtal::FrontierEvolver2 +::getOuterPoint(const Surfel& s) const +{ + return myKSpace.sCoords( myKSpace.sIndirectIncident( s, *myKSpace.sOrthDirs( s ) ) ); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Implementation of inline functions // + +template +inline +std::ostream& +DGtal::operator<< ( std::ostream & out, + const FrontierEvolver2 & object ) +{ + object.selfDisplay( out ); + return out; +} + +// // +/////////////////////////////////////////////////////////////////////////////// + + diff --git a/deformations/FrontierEvolverHelpers.h b/deformations/FrontierEvolverHelpers.h new file mode 100644 index 0000000..e0a6d22 --- /dev/null +++ b/deformations/FrontierEvolverHelpers.h @@ -0,0 +1,123 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +#pragma once + +/** + * @file FrontierEvolverHelpers.h + * + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * @date 2012/04/19 + * + * This file is part of the DGtal library. + */ + +#if defined(FrontierEvolverHelpers_RECURSES) +#error Recursive header files inclusion detected in FrontierEvolverHelpers.h +#else // defined(FrontierEvolverHelpers_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define FrontierEvolverHelpers_RECURSES + +#if !defined FrontierEvolverHelpers_h +/** Prevents repeated inclusion of headers. */ +#define FrontierEvolverHelpers_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include "DGtal/base/Common.h" +////////////////////////////////////////////////////////////////////////////// + + +//#define WITHINFO + +namespace DGtal +{ + + + //------------------------------------------------------------------------------ + template +struct SetFromImageDomainValueTraits + { + public: + typedef DigitalSetBySTLSet Set; + public: + public: + static Set get(I& aImage) + { + return Set(aImage.domain()); + } + }; + //Partial specialization + template +struct SetFromImageDomainValueTraits< + ImageContainerBySTLMap, + D, V > + { + public: + typedef DigitalSetFromMap > Set; + public: + static Set get(ImageContainerBySTLMap& aImage) + { + return Set(aImage); + } + }; + //------------------------------------------------------------------------------ + template +struct SetFromImageSelector + { + public: + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + typedef typename I::Domain Domain; + typedef typename I::Value Value; + + typedef typename SetFromImageDomainValueTraits::Set Set; + + public: + static Set get(I& i) + { + return SetFromImageDomainValueTraits::get(i); + } + }; + + //------------------------------------------------------------------------------ + namespace details + { + class CompareSecondElement { + public: + template + bool operator()(const T& a, const T& b) + { + return ( a.second < b.second ); + } + }; + + } + +} // namespace DGtal + + +/////////////////////////////////////////////////////////////////////////////// +// Includes inline functions. +//#include "FrontierEvolverHelpers.ih" + +// // +/////////////////////////////////////////////////////////////////////////////// + +#endif // !defined FrontierEvolverHelpers_h + +#undef FrontierEvolverHelpers_RECURSES +#endif // else defined(FrontierEvolverHelpers_RECURSES) diff --git a/deformations/GrayscaleMapCast.h b/deformations/GrayscaleMapCast.h new file mode 100644 index 0000000..ba3a375 --- /dev/null +++ b/deformations/GrayscaleMapCast.h @@ -0,0 +1,22 @@ +#ifndef DIGITALSNOW_DEFORMATIONS_GRAYSCALEMAPCAST_HPP +#define DIGITALSNOW_DEFORMATIONS_GRAYSCALEMAPCAST_HPP + +// Grayscale map returning value casted to specified type +template +class GrayscaleMapCast +{ +public: + GrayscaleMapCast() : m_min(0), m_max(255) {} + GrayscaleMapCast(T min_value, T max_value) : m_min(min_value), m_max(max_value) {} + + inline + unsigned char operator() (T value) const + { + return static_cast( (255*(value - m_min)) / (m_max - m_min) ); + } + +private: + T m_min, m_max; +}; + +#endif // DIGITALSNOW_DEFORMATIONS_GRAYSCALEMAPCAST_HPP diff --git a/deformations/IFFT.h b/deformations/IFFT.h index e1960f4..da3b308 100644 --- a/deformations/IFFT.h +++ b/deformations/IFFT.h @@ -20,7 +20,7 @@ namespace DGtal{ { //ASSERT - BOOST_CONCEPT_ASSERT(( CImage )); + BOOST_CONCEPT_ASSERT(( concepts::CImage )); BOOST_STATIC_ASSERT((boost::is_same< typename TImage::Value, std::complex >::value)); diff --git a/deformations/IFFT.ih b/deformations/IFFT.ih index 3e91e91..353fe34 100644 --- a/deformations/IFFT.ih +++ b/deformations/IFFT.ih @@ -33,10 +33,10 @@ DGtal::IFFT::compute(TOutputImage& anImage) Vector v = myImage.extent(); int t[dimension]; int n = 1; - for (unsigned int i= 0; i < dimension; ++i) - { - t[i] = v.at(i); - n *= t[i]; + for (int i = 0; i < dimension; ++i) + { //row-major order + t[dimension-1-i] = v[i]; + n *= v[i]; } //using fftw library diff --git a/deformations/LieSplittingEvolver.h b/deformations/LieSplittingEvolver.h index de0135f..5cd40da 100644 --- a/deformations/LieSplittingEvolver.h +++ b/deformations/LieSplittingEvolver.h @@ -71,8 +71,8 @@ namespace DGtal //concepts to write //ASSERT - //BOOST_CONCEPT_ASSERT(( CImplicitEvolver )); - //BOOST_CONCEPT_ASSERT(( CImplicitEvolver )); + //BOOST_CONCEPT_ASSERT(( concepts::CImplicitEvolver )); + //BOOST_CONCEPT_ASSERT(( concepts::CImplicitEvolver )); // ----------------------- Types ------------------------------ public: diff --git a/deformations/LocalBalloonForce.h b/deformations/LocalBalloonForce.h new file mode 100644 index 0000000..77e8354 --- /dev/null +++ b/deformations/LocalBalloonForce.h @@ -0,0 +1,123 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +#pragma once + +/** + * @file LocalBalloonForce.h + * + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * @date 2012/03/01 + * + * This files contains several basic classes representing Functors + * on points. + * + * This file is part of the DGtal library. + */ + +#if defined(LocalBalloonForce_RECURSES) +#error Recursive header files inclusion detected in LocalBalloonForce.h +#else // defined(LocalBalloonForce_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define LocalBalloonForce_RECURSES + +#if !defined LocalBalloonForce_h +/** Prevents repeated inclusion of headers. */ +#define LocalBalloonForce_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include "DGtal/kernel/CPointFunctor.h" +////////////////////////////////////////////////////////////////////////////// + +namespace DGtal +{ + + ///////////////////////////////////////////////////////////////////////////// + // template class LocalBalloonForce + /** + * Description of template class 'LocalBalloonForce'

+ * \brief Aim: Functor that maps a point + * to a velocity computed from an implicit function + * and some extern data (a balloon force and + * an extern scalar field for weighting). + * + * @tparam TFunction type of implicit function, + * which a model of point functor + * @tparam TExternField type of the extern field, + * which a model of point functor too + */ + template + struct LocalBalloonForce + { + typedef TFunction PointFunctor; + typedef TExternField ExternField; + + BOOST_CONCEPT_ASSERT(( concepts::CPointFunctor )); + BOOST_CONCEPT_ASSERT(( concepts::CPointFunctor )); + BOOST_STATIC_ASSERT + (( ConceptUtils::SameType< typename PointFunctor::Point, + typename ExternField::Point>::value )); + + typedef typename PointFunctor::Point Point; + typedef double Value; + + /** + * Constructor + */ + LocalBalloonForce(PointFunctor& aF1, const ExternField& aF2, const double& aK = 0); + + /** + * Main operator + * @param aPoint any point. + * @tparam TInputPoint type of point + * @return the velocity at @a aPoint. + */ + template + double operator()( const TInputPoint& aPoint ) const; + + private: + /** + * Aliasing pointer to the implicit function + */ + PointFunctor* myFuncPtr; + /** + * Constant aliasing pointer to the extern field + */ + const ExternField* myFieldPtr; + /** + * Balloon force + */ + double myK; + + }; // end of class LocalBalloonForce + + +} // namespace DGtal + + +/////////////////////////////////////////////////////////////////////////////// +// Includes inline functions. +#include "LocalBalloonForce.ih" + +// // +/////////////////////////////////////////////////////////////////////////////// + +#endif // !defined LocalBalloonForce_h + +#undef LocalBalloonForce_RECURSES +#endif // else defined(LocalBalloonForce_RECURSES) diff --git a/deformations/LocalBalloonForce.ih b/deformations/LocalBalloonForce.ih new file mode 100644 index 0000000..61c87da --- /dev/null +++ b/deformations/LocalBalloonForce.ih @@ -0,0 +1,68 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +/** + * @file LocalBalloonForce.ih + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * @date 2012/02/02 + * + * Implementation of inline methods defined in LocalBalloonForce.h + * + * This file is part of the DGtal library. + */ + + +////////////////////////////////////////////////////////////////////////////// +#include +////////////////////////////////////////////////////////////////////////////// + +#include "DGtal/images/DifferentialOperators.h" + +/////////////////////////////////////////////////////////////////////////////// +// IMPLEMENTATION of inline methods. +/////////////////////////////////////////////////////////////////////////////// +template +inline +DGtal::LocalBalloonForce +::LocalBalloonForce(PointFunctor& aF1, const ExternField& aF2, const double& aK) + : myFuncPtr( &aF1 ), myFieldPtr( &aF2 ), myK( aK ) +{ +} + +template +template +inline +double +DGtal::LocalBalloonForce +::operator()( const TInputPoint& aPoint ) const +{ + ASSERT( myFuncPtr ); + PointFunctor* tmpPtr = const_cast(myFuncPtr); +//ugly until differential operators are updated + GodunovGradient gradient( *tmpPtr, (myK <= 0), 1 ); + GradientModulus > m(gradient); + double res = myK * static_cast( (*myFieldPtr)(aPoint) ) + * static_cast( m(aPoint) ); + return res; +} +//------------------------------------------------------------------------------ + + +// // +/////////////////////////////////////////////////////////////////////////////// + + diff --git a/deformations/LocalMCM.h b/deformations/LocalMCM.h new file mode 100644 index 0000000..d68d77c --- /dev/null +++ b/deformations/LocalMCM.h @@ -0,0 +1,153 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +#pragma once + +/** + * @file LocalMCM.h + * + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * @date 2012/03/01 + * + * This files contains several basic classes representing Functors + * on points. + * + * This file is part of the DGtal library. + */ + +#if defined(LocalMCM_RECURSES) +#error Recursive header files inclusion detected in LocalMCM.h +#else // defined(LocalMCM_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define LocalMCM_RECURSES + +#if !defined LocalMCM_h +/** Prevents repeated inclusion of headers. */ +#define LocalMCM_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include "DGtal/kernel/CPointFunctor.h" + +#include "DifferentialOperators.h" +////////////////////////////////////////////////////////////////////////////// + +namespace DGtal +{ + + ///////////////////////////////////////////////////////////////////////////// + // template class LocalMCM + /** + * Description of template class 'LocalMCM'

+ * \brief Aim: Functor that maps a point + * to a velocity computed from an implicit function + * and some extern data (two extern scalar fields + * for weighting). + * + * @tparam TFunction type of implicit function, + * which a model of point functor + * @tparam TExternField type of the extern field, + * which a model of point functor too + */ + template + struct LocalMCM + { + typedef TFunction PointFunctor; + typedef TExternField ExternField; + + BOOST_CONCEPT_ASSERT(( concepts::CPointFunctor )); + BOOST_CONCEPT_ASSERT(( concepts::CPointFunctor )); + BOOST_STATIC_ASSERT + (( ConceptUtils::SameType< typename PointFunctor::Point, + typename ExternField::Point>::value )); + + typedef typename PointFunctor::Point Point; + typedef double Value; + + typedef typename Gradient >::OutputValue Normal; + typedef typename Divergence >::OutputValue Curvature; + + /** + * Constructor + */ + LocalMCM(PointFunctor& aF1, const ExternField& aA, const ExternField& aB); + + /** + * Constructor + */ + ~LocalMCM() + { + } + + /** + * Main operator + * @param aPoint any point. + * @tparam TInputPoint type of point + * @return the velocity at @a aPoint. + */ + template + double operator()( const TInputPoint& aPoint ) const; + + /** + * Gradient. + * @param aPoint any point. + * @tparam TInputPoint type of point + * @return the gradient at @a aPoint. + */ + template + Normal getNormal( const TInputPoint& aPoint ) const; + + /** + * Curvature. + * @param aPoint any point. + * @tparam TInputPoint type of point + * @return curvature at @a aPoint. + */ + template + Curvature getCurvature( const TInputPoint& aPoint ) const; + + private: + /** + * Aliasing pointer to the implicit function + */ + const PointFunctor* myFuncPtr; + /** + * Constant aliasing pointer to the extern field A + */ + const ExternField* myAPtr; + /** + * Constant aliasing pointer to the extern field B + */ + const ExternField* myBPtr; + + }; // end of class LocalMCM + + +} // namespace DGtal + + +/////////////////////////////////////////////////////////////////////////////// +// Includes inline functions. +#include "LocalMCM.ih" + +// // +/////////////////////////////////////////////////////////////////////////////// + +#endif // !defined LocalMCM_h + +#undef LocalMCM_RECURSES +#endif // else defined(LocalMCM_RECURSES) diff --git a/deformations/LocalMCM.ih b/deformations/LocalMCM.ih new file mode 100644 index 0000000..70c03b0 --- /dev/null +++ b/deformations/LocalMCM.ih @@ -0,0 +1,115 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +/** + * @file LocalMCM.ih + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * @date 2012/02/02 + * + * Implementation of inline methods defined in LocalMCM.h + * + * This file is part of the DGtal library. + */ + + +////////////////////////////////////////////////////////////////////////////// +#include +////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// IMPLEMENTATION of inline methods. +/////////////////////////////////////////////////////////////////////////////// +template +inline +DGtal::LocalMCM +::LocalMCM(PointFunctor& aF1, const ExternField& aA, const ExternField& aB) + : myFuncPtr( &aF1 ), myAPtr( &aA ), myBPtr( &aB ) +{ +} + +template +template +inline +double +DGtal::LocalMCM +::operator()( const TInputPoint& aPoint ) const +{ + ASSERT( myFuncPtr ); + PointFunctor* tmpFuncPtr = const_cast(myFuncPtr); + PointFunctor* tmpBPtr = const_cast(myBPtr); +//ugly until differential operators are updated + +//gradient modulus + CentralDifference cdiff( *tmpFuncPtr ); + Gradient > cg( cdiff ); + GradientModulus > > cm(cg); + +//mean curvature + //TODO allow different types for implicit function and extern field + //in weightedDifference + WeightedDifference2 diff2( *tmpFuncPtr, *tmpBPtr ); + Divergence > div(diff2); + double res = static_cast( (*myAPtr)(aPoint) ) + * static_cast( cm(aPoint) ) + * static_cast( div(aPoint) ); + + return res; +} +//------------------------------------------------------------------------------ +template +template +inline +typename DGtal::LocalMCM::Normal +DGtal::LocalMCM +::getNormal( const TInputPoint& aPoint ) const +{ + ASSERT( myFuncPtr ); + PointFunctor* tmpFuncPtr = const_cast(myFuncPtr); +//ugly until differential operators are updated + +//gradient + CentralDifference cdiff( *tmpFuncPtr ); + Gradient > cg( cdiff ); + return cg( aPoint ); +} + +//------------------------------------------------------------------------------ +template +template +inline +typename DGtal::LocalMCM::Curvature +DGtal::LocalMCM +::getCurvature( const TInputPoint& aPoint ) const +{ + ASSERT( myFuncPtr ); + PointFunctor* tmpFuncPtr = const_cast(myFuncPtr); + PointFunctor* tmpBPtr = const_cast(myBPtr); +//ugly until differential operators are updated + +//mean curvature + //TODO allow different types for implicit function and extern field + //in weightedDifference + WeightedDifference2 diff2( *tmpFuncPtr, *tmpBPtr ); + Divergence > div(diff2); + return div( aPoint ); +} + +// // +/////////////////////////////////////////////////////////////////////////////// + + diff --git a/deformations/MultiPhaseField.h b/deformations/MultiPhaseField.h new file mode 100644 index 0000000..c2d51f2 --- /dev/null +++ b/deformations/MultiPhaseField.h @@ -0,0 +1,256 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +#pragma once + +/** + * @file MultiPhaseField.h + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * + * @date 2012/07/12 + * + * Header file for module MultiPhaseField.cpp + * + * This file is part of the DGtal library. + */ + +#if defined(MultiPhaseField_RECURSES) +#error Recursive header files inclusion detected in MultiPhaseField.h +#else // defined(MultiPhaseField_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define MultiPhaseField_RECURSES + +#if !defined MultiPhaseField_h +/** Prevents repeated inclusion of headers. */ +#define MultiPhaseField_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include +#include + +#include "DGtal/base/Common.h" +#include "DGtal/images/CImage.h" + +#include "DGtal/base/CowPtr.h" +#include "DGtal/base/CountedPtr.h" + + +//for getSignedDistance private method +#include +#include + +#include "deformationFunctions.h" +////////////////////////////////////////////////////////////////////////////// + +namespace DGtal +{ + + + ///////////////////////////////////////////////////////////////////////////// + // template class MultiPhaseField + /** + * Description of template class 'MultiPhaseField'

+ * \brief Aim: This class is a way of deforming an image of labels. + * Each region (ie. set of points having a same label) is viewed as + * the set of points having a value greater than 0.5 for a given phase field. + * Each region is evolved through its phase field. + * + * @tparam TLabelImage a model of CImage (storing labels) + * @tparam TFieldImage a model of CImage (storing phase field values) + * @tparam TEvolver a model of phase field evolver + */ + template + class MultiPhaseField + { + + // ----------------------- Types check ----------------------- + + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + BOOST_STATIC_ASSERT + (( ConceptUtils::SameType< typename TLabelImage::Point, + typename TFieldImage::Point>::value )); + + + // ----------------------- Types ------------------------------ + public: + + /// Image of labels + typedef TLabelImage LabelImage; + typedef typename LabelImage::Value Label; + typedef typename LabelImage::Domain Domain; + typedef typename Domain::Point Point; + + /// Images of phase field values + typedef TFieldImage FieldImage; + typedef CowPtr FieldImagePtr; + + + /// Phase field evolver + typedef TEvolver Evolver; + + // ------------------------- Protected Datas ------------------------------ + protected: + // ------------------------- Private Datas -------------------------------- + private: + + + // ------------------------- References -------------------------------- + /** + * Reference on the image of labels + */ + LabelImage& myLabelImage; + /** + * Reference on the evolver + */ + Evolver& myEvolver; + + + // ------------------------- Data -------------------------------- + /** + * Default label for points that does not belong to any region + * after some evolution steps + */ + Label myDefaultLabel; + /** + * List of smart owning pointers on phase fields + */ + std::vector myFields; + /** + * List of labels + */ + std::vector

+ * \brief Aim: This class is a way of deforming an image of labels + * around all the connected contact surfaces between two adjacent regions, + * according to a speed field, whose computation is delegated to a point functor. + * + * + * @tparam TKSpace a model of CCellularGridSpaceND + * @tparam TLabelImage a model of CImage (storing labels) + * @tparam TDistanceImage a model of CImage (storing distance values) + * @tparam TExternImage a model of CImage (storing an extern scalar field) + * @tparam TTopoPredicate + */ + template + class PartitionEvolver + { + + + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + BOOST_STATIC_ASSERT + (( ConceptUtils::SameType< typename TKSpace::Point, + typename TLabelImage::Point>::value )); + BOOST_STATIC_ASSERT + (( ConceptUtils::SameType< typename TKSpace::Point, + typename TDistanceImage::Point>::value )); + + BOOST_CONCEPT_ASSERT(( concepts::CImage )); + BOOST_STATIC_ASSERT + (( ConceptUtils::SameType< typename TKSpace::Point, + typename TExternImage::Point>::value )); + + // BOOST_CONCEPT_ASSERT(( CPoinTTopoPredicate )); + // BOOST_STATIC_ASSERT + // (( ConceptUtils::SameType< typename TKSpace::Point, + // typename TTopoPredicate::Point>::value )); + //TODO testing TTopoPredicate as a binary TopoPredicate on points and labels + + // ----------------------- Types ------------------------------ + public: + + /// Khalimsky space + typedef TKSpace KSpace; + typedef typename KSpace::Cell Cell; + typedef typename KSpace::SCell SCell; + + /// Image of labels + typedef TLabelImage LabelImage; + typedef typename LabelImage::Value Label; + typedef typename LabelImage::Domain Domain; + typedef typename Domain::Point Point; + + /// Images of distance values + typedef TDistanceImage DistanceImage; + typedef CountedPtr DistanceImagePtr; + + /// Extern image + typedef TExternImage ExternImage; + + /// Point functor for the mapping points-speed + typedef LocalMCM Functor; + typedef CountedPtr FunctorPtr; + + /// Topological predicate + typedef TTopoPredicate TopoPredicate; + + /// Frontier evolver + typedef FrontierEvolver2 Evolver; + typedef CountedPtr EvolverPtr; + + + // ----------------------- Standard services ------------------------------ + public: + + /** + * Constructor. + * @param aK khalimsky space where the digital frontiers are defined + * @param aI an image of labels + * @param aF any extern data field + * @param aP any point TopoPredicate + */ + PartitionEvolver(const KSpace& aK, LabelImage& aI, const ExternImage& aF, const TopoPredicate& aP); + + /** + * Destructor. Does nothing. + */ + ~PartitionEvolver(); + + + /** + * Deform the image of labels during @a aT + * + * @param aT time step + * @return time spent during the deformation + * (equal to aT). + */ + double update(const double& aT); + + /** + * Check the validity of the different frontiers + * of the partition + * + * @param aPtrToCaller pointer on the frontier evolver + * that called the checking procedure + * @param aPoint any point + * @pre must lie in the domain of the image of labels + */ + void checkFrontiers(const Evolver* aPtrToCaller, const Point& aPoint); + + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const; + + + /** + * Writes/Displays the object on an output stream. + * @param out the output stream where the object is written. + */ + void selfDisplay ( std::ostream & out ) const; + + + // ------------------------- Protected Datas ------------------------------ + protected: + // ------------------------- Private Datas -------------------------------- + private: + + + // ------------------------- References -------------------------------- + /** + * Constant reference on the khalimsky space + */ + const KSpace& myKSpace; + /** + * Reference on the image of labels + */ + LabelImage& myLabelImage; + /** + * Constant reference on the extern image + */ + const ExternImage& myExternImage; + /** + * Constant reference on the topological predicate + */ + const TopoPredicate& myTopoPred; + + + // ------------------------- Data -------------------------------- + /** + * Set of smart owning pointers on images (of distance values) + */ + std::vector myImages; + /** + * Set of smart owning pointers on functors, + * which locally computes the differential estimations + * and displacement speed + */ + std::vector myFunctors; + /** + * Set of smart owning pointers on frontier evolvers + */ + std::vector myEvolvers; + + // ------------------------- Hidden services ------------------------------ + protected: + + + /** + * Copy constructor. + * @param other the object to clone. + * Forbidden by default. + */ + PartitionEvolver ( const PartitionEvolver & other ); + + /** + * Assignment. + * @param other the object to copy. + * @return a reference on 'this'. + * Forbidden by default. + */ + PartitionEvolver & operator= ( const PartitionEvolver & other ); + + private: + + + + // ------------------------- Internals ------------------------------------ + private: + + /** + * Insert bels, ie cells lying between two points + * of different labels in @a aImg, + * in @a aSet + * @param aSet (returned) set of bels. + * @param aKSpace khalimsky space. + * @param aImg label image. + * @param aLowerBound. + * @param aUpperBound. + */ + void getBels ( std::set& aSet, + const KSpace & aKSpace, + const LabelImage & aImg, + const Point & aLowerBound, + const Point & aUpperBound ); + + }; // end of class PartitionEvolver + + + /** + * Overloads 'operator<<' for displaying objects of class 'PartitionEvolver'. + * @param out the output stream where the object is written. + * @param object the object of class 'PartitionEvolver' to write. + * @return the output stream after the writing. + */ + template + std::ostream& + operator<< ( std::ostream & out, + const PartitionEvolver & object ); + + +} // namespace DGtal + + +/////////////////////////////////////////////////////////////////////////////// +// Includes inline functions. +#include "FrontierEvolver.h" +#include "FrontierEvolver2.h" +#include "PartitionEvolver.ih" + +// // +/////////////////////////////////////////////////////////////////////////////// + +#endif // !defined PartitionEvolver_h + +#undef PartitionEvolver_RECURSES +#endif // else defined(PartitionEvolver_RECURSES) diff --git a/deformations/PartitionEvolver.ih b/deformations/PartitionEvolver.ih new file mode 100644 index 0000000..4e5d746 --- /dev/null +++ b/deformations/PartitionEvolver.ih @@ -0,0 +1,234 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +/** + * @file PartitionEvolver.ih + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * + * @date 2012/04/06 + * + * @brief Implementation of inline methods defined in PartitionEvolver.h + * + * This file is part of the DGtal library. + */ + + + +////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// IMPLEMENTATION of inline methods. +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// ----------------------- Standard services ------------------------------ + +// frontier +#include "DGtal/topology/SurfelAdjacency.h" +#include "DGtal/topology/helpers/FrontierPredicate.h" +#include "DGtal/topology/LightExplicitDigitalSurface.h" +#include "DGtal/topology/SCellsFunctors.h" + +template +inline +DGtal::PartitionEvolver +::PartitionEvolver(const KSpace& aK, LabelImage& aI, const ExternImage& aF, const TopoPredicate& aP) + : myKSpace( aK ), myLabelImage( aI ), myExternImage( aF ), myTopoPred( aP ) +{ + //maximal number of frontiers expected + const unsigned int nb = 100; + myImages.reserve(nb); + myFunctors.reserve(nb); + myEvolvers.reserve(nb); + + //domain + Domain d = Domain( myLabelImage.domain().lowerBound(), myLabelImage.domain().upperBound() ); + //ASSERT( d == myExternImage.domain() ); operator == does not exist in HyperRectDomain + + /// retrieve bels + std::set bels; + getBels( bels, myKSpace, myLabelImage, myKSpace.lowerBound(), myKSpace.upperBound() ); + + /// retrieve frontiers + while(!bels.empty()){ + + //unsigned bel + Cell ubel = *(bels.begin()); + //signed version (arbitrary choice) + SCell sbel = myKSpace.signs( ubel, true ); + //incident points + SCellToIncidentPoints func( myKSpace ); + typename SCellToIncidentPoints::Output points = func( sbel ); + Label iLabel( myLabelImage( points.first ) ); + Label oLabel( myLabelImage( points.second ) ); + + /// frontier from sbel + typedef FrontierPredicate SurfelPredicate; + /// !!!!!! be careful oLabel and iLabel are swaped because func is wrong + /// => Seems to have been corrected since ... + SurfelPredicate surfelPred( myKSpace, myLabelImage, iLabel, oLabel ); + typedef LightExplicitDigitalSurface Frontier; + Frontier frontier( myKSpace, + surfelPred, + SurfelAdjacency( true ), + sbel ); + + // tracking (and removing bels belonging to this frontier) + typedef typename Frontier::SurfelConstIterator SurfelIterator; + for ( SurfelIterator it = frontier.begin(), + itEnd = frontier.end(); + it != itEnd; ++it ) + bels.erase( myKSpace.unsigns( *it ) ); + + /// new frontier evolver (with its image and differential estimators) + myImages.push_back( DistanceImagePtr( new DistanceImage( d ) ) ); + myFunctors.push_back( FunctorPtr( new Functor( *(*myImages.rbegin()), + myExternImage, + myExternImage ) ) ); + myEvolvers.push_back( EvolverPtr( new Evolver(myKSpace, + myLabelImage, + *(*myImages.rbegin()), + sbel, + *(*myFunctors.rbegin()), + myTopoPred, this) ) ); + } + + ASSERT( myImages.size() == myFunctors.size() ); + ASSERT( myImages.size() == myEvolvers.size() ); + +} + +template +inline +DGtal::PartitionEvolver +::~PartitionEvolver() +{ +} + + + +template +inline +double +DGtal::PartitionEvolver +::update(const double& aT) +{ + + /// update all frontiers (in arbitrary order) + for (typename std::vector::const_iterator + it = myEvolvers.begin(), + itEnd = myEvolvers.end(); it != itEnd; ++it) + { + (*it)->update( aT ); + } + + return aT; +} + +template +inline +void +DGtal::PartitionEvolver +::checkFrontiers(const Evolver* aPtrToCaller, const Point& aPoint) +{ + for (typename std::vector::const_iterator + it = myEvolvers.begin(), + itEnd = myEvolvers.end(); it != itEnd; ++it) + { /// for each frontier (except aPtrToCaller) + if ( it->get() != aPtrToCaller ) + { + (*it)->updateFrontier( aPoint ); + } + } +} + +template +inline +void +DGtal::PartitionEvolver +::selfDisplay ( std::ostream & out ) const +{ + out << "[PartitionEvolver] "; + out << myEvolvers.size() << " frontier(s) "; +} + +template +inline +bool +DGtal::PartitionEvolver +::isValid() const +{ + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// Internals // + +template +inline +void +DGtal::PartitionEvolver +::getBels( std::set& aSet, + const KSpace & aKSpace, + const LabelImage & aImg, + const Point & aLowerBound, + const Point & aUpperBound ) +{ + for (DGtal::Dimension k = 0; k < KSpace::dimension; ++k ) + { + Cell dir_low_uid = aKSpace.uSpel( aLowerBound ); + Cell dir_up_uid = aKSpace.uGetDecr( aKSpace.uSpel( aUpperBound ), k); + Cell p = dir_low_uid; + do + { + Label here = aImg( aKSpace.uCoords(p) ); + Label next = aImg( aKSpace.uCoords(aKSpace.uGetIncr( p, k )) ); + if ( here != next ) + { // add new bel to the set. + aSet.insert( aKSpace.uIncident( p, k, true )); + } + } + while ( aKSpace.uNext( p, dir_low_uid, dir_up_uid ) ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Implementation of inline functions // + +template +inline +std::ostream& +DGtal::operator<< ( std::ostream & out, + const PartitionEvolver & object ) +{ + object.selfDisplay( out ); + return out; +} + +// // +/////////////////////////////////////////////////////////////////////////////// + + diff --git a/deformations/PointPredicates.h b/deformations/PointPredicates.h new file mode 100644 index 0000000..fc220e4 --- /dev/null +++ b/deformations/PointPredicates.h @@ -0,0 +1,334 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +#pragma once + +/** + * @file PointPredicates.h + * + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * @date 2012/02/02 + * + * This files contains several basic classes representing binary predicates + * + * This file is part of the DGtal library. + */ + +#if defined(PointPredicates_RECURSES) +#error Recursive header files inclusion detected in PointPredicates.h +#else // defined(PointPredicates_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define PointPredicates_RECURSES + +#if !defined PointPredicates_h +/** Prevents repeated inclusion of headers. */ +#define PointPredicates_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include "DGtal/base/Common.h" +////////////////////////////////////////////////////////////////////////////// + +namespace DGtal +{ + + ///////////////////////////////////////////////////////////////////////////// + // template class CascadingPointPredicate + /** + * Description of template class 'CascadingPointPredicate'

\brief + * Aim: The predicate returns true when the given binary functor + * returns true for the two PointPredicates given at construction. + * + * @tparam PointPredicate1 the left predicate type. + * @tparam PointPredicate2 the right predicate type. + */ + template + struct CascadingPointPredicate + { + typedef TPointPredicate1 PointPredicate1; + typedef TPointPredicate2 PointPredicate2; + typedef typename PointPredicate1::Point Point; + // should be the same. + BOOST_STATIC_ASSERT ((boost::is_same< Point, typename PointPredicate2::Point >::value)); + typedef typename PointPredicate2::Point Point2; + + /** + Constructor from predicates + @param pred1 the left predicate. + @param pred2 the right predicate. + */ + CascadingPointPredicate( const PointPredicate1 & pred1, + const PointPredicate2 & pred2 ); + + /** + Copy constructor. + @param other the object to copy + */ + CascadingPointPredicate( const CascadingPointPredicate& other ); + + /** + Assignement + @param other the object to copy + @return reference to the current object + */ + CascadingPointPredicate& operator=( const CascadingPointPredicate& other ); + + /** + Destructor + */ + ~CascadingPointPredicate(); + + /** + * @param p any point. + * @return the value of the predicate at this point. + */ + bool operator()( const Point & p ) const; + + /// aliasing pointer to the left predicate. + const PointPredicate1* myPred1; + /// aliasing pointer to the right predicate. + const PointPredicate2* myPred2; + }; + +//------------------------------------------------------------------------------ +template +inline +DGtal::CascadingPointPredicate +::CascadingPointPredicate( const PointPredicate1 & pred1, + const PointPredicate2 & pred2 ) + : myPred1( &pred1 ), myPred2( &pred2 ) +{ +} +//------------------------------------------------------------------------------ +template +inline +DGtal::CascadingPointPredicate +::CascadingPointPredicate( const CascadingPointPredicate& other ) + : myPred1( other.pred1 ), myPred2( other.pred2 ) +{ +} +//------------------------------------------------------------------------------ +template +inline +DGtal::CascadingPointPredicate& +DGtal::CascadingPointPredicate +::operator=( const CascadingPointPredicate& other ) +{ + if (this != &other) + { + myPred1 = other.myPred1; + myPred2 = other.myPred2; + } + return *this; +} +//------------------------------------------------------------------------------ +template +inline +DGtal::CascadingPointPredicate +::~CascadingPointPredicate() +{ +} +//------------------------------------------------------------------------------ +template +inline +bool +DGtal::CascadingPointPredicate +::operator()( const Point & p ) const +{ + if ( myPred1->operator()( p ) ) + return myPred2->operator()( p ); + else return false; +} + + + ///////////////////////////////////////////////////////////////////////////// + // template class OneLabelPredicate + /** + * \brief Aim: Predicate returning true or false + * according to the comparison of a label with a reference label. + * + * @tparam TLabel type of labels + (must be default-constructible and equally-comparable) + * @tparam TBinaryPredicate type of binary predicate + (must have a () operator taking two input parameters + and returning a boolean). + * + */ + template + class OneLabelPredicate + { + public: + typedef typename TImage::Value Value; + typedef typename TImage::Point Point; + + public: + /** + * Constructor + * @param aImg any image + * @param aLabel any label + * @param aFunctor any binary functor + */ + OneLabelPredicate(const TImage& aImg, const Value& aLabel, + const TBinaryPredicate& aFunctor = TBinaryPredicate() ) + : myImg(aImg), myLabel(aLabel), myF(aFunctor) + { + } + + private: + /** + * Reference on an image + */ + const TImage& myImg; + /** + * Value to compare with + */ + Value myLabel; + /** + * Comparison method + */ + TBinaryPredicate myF; + + public: + /** + * Compare @a aLabel to @a myLabel with @a myF + * @param aPoint any point of the image domain + * whose label has to be compared to @a myLabel + * @return true or false + */ + bool operator()(const Point& aPoint) const + { + return myF(myLabel, myImg(aPoint) ); + } + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const + { + return true; + } + /** + * Writes/Displays the object on an output stream. + * @param out the output stream where the object is written. + */ + void selfDisplay ( std::ostream & out ) const + { + out << "(" << myLabel << ")"; + } + + }; + + + ///////////////////////////////////////////////////////////////////////////// + // template class TwoLabelsPredicate + /** + * \brief Aim: Predicate returning true or false + * according to the comparison of a label with + * two reference labels. + * + * @tparam TLabel type of labels + (must be default-constructible and equally-comparable) + * @tparam TBinaryPredicate type of binary predicate + (must have a () operator taking two input parameters + and returning a boolean). + * + */ + template + class TwoLabelsPredicate + { + + public: + typedef typename TImage::Value Value; + typedef typename TImage::Point Point; + + public: + /** + * Constructor + * @param aImg any image + * @param aLabel1 any value + * @param aLabel2 any value + * @param aFunctor any binary functor + */ + TwoLabelsPredicate(const TImage& aImg, + const Value& aLabel1, const Value& aLabel2, + const TBinaryPredicate& aFunctor = TBinaryPredicate() ) + : myImg(aImg), myLabel1(aLabel1), myLabel2(aLabel2), myF(aFunctor) + { + } + + private: + /** + * Reference on an image + */ + const TImage& myImg; + /** + * Value to compare with + */ + Value myLabel1; + /** + * Value to compare with + */ + Value myLabel2; + /** + * Comparison method + */ + TBinaryPredicate myF; + + public: + /** + * Compare the label of @a aPoint to @a myLabel1 and @a myLabel2 + * with @a myF + * @param aPoint any point whose label has to be compared + * @return true or false + */ + bool operator()(const Point& aPoint) const + { + return ( myF(myLabel1, myImg(aPoint)) || myF(myLabel2, myImg(aPoint)) ); + } + /** + * Checks the validity/consistency of the object. + * @return 'true' if the object is valid, 'false' otherwise. + */ + bool isValid() const + { + return true; + } + /** + * Writes/Displays the object on an output stream. + * @param out the output stream where the object is written. + */ + void selfDisplay ( std::ostream & out ) const + { + out << "(" << myLabel1 << " U " << myLabel2 << ")"; + } + }; + + +} // namespace DGtal + + +/////////////////////////////////////////////////////////////////////////////// +// Includes inline functions. +//#include "PointPredicates.ih" + +// // +/////////////////////////////////////////////////////////////////////////////// + +#endif // !defined PointPredicates_h + +#undef PointPredicates_RECURSES +#endif // else defined(PointPredicates_RECURSES) diff --git a/deformations/SimplePointHelper.h b/deformations/SimplePointHelper.h new file mode 100644 index 0000000..3a42f05 --- /dev/null +++ b/deformations/SimplePointHelper.h @@ -0,0 +1,303 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +#pragma once + +/** + * @file SimplePointHelper.h + * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr ) + * Laboratoire d'InfoRmatique en LabelsImage et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * + * @date 2012/04/05 + * + * @brief Header file for module SimplePointHelper.cpp + * + * This file is part of the DGtal library. + */ + +#if defined(SimplePointHelper_RECURSES) +#error Recursive header files inclusion detected in SimplePointHelper.h +#else // defined(SimplePointHelper_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define SimplePointHelper_RECURSES + +#if !defined SimplePointHelper_h +/** Prevents repeated inclusion of headers. */ +#define SimplePointHelper_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions + +#include +#include +#include +#include +#include +#include + +#include "DGtal/base/Common.h" +#include "DGtal/kernel/SpaceND.h" +#include "DGtal/kernel/domains/HyperRectDomain.h" +#include "DGtal/base/CLabel.h" + +//simplicity +#include "DGtal/topology/MetricAdjacency.h" +#include "DGtal/topology/DomainMetricAdjacency.h" +#include "DGtal/topology/DomainAdjacency.h" +#include "DGtal/topology/DigitalTopology.h" +#include "DGtal/topology/Object.h" + +////////////////////////////////////////////////////////////////////////////// + +namespace DGtal +{ + +#include "SimplePointHelperDetails.ih" + + ///////////////////////////////////////////////////////////////////////////// + // template class SimplePointHelper + /** + * \brief Aim: Tests whether a given voxel v, + * which belongs to a region V, + * is ML-simple with respect to a given region R. + * + * In short, v can belong to R if it is a simple point + * for all groups of up to three regions including either V or R. + * + * [Bertrand, 1994] A voxel v is simple for a region X + * iff #C6 [G6(v, X)] = #C18[G18(v, X^c)] = 1, + * where #Ck [Y] denotes the number of k-connected components of a set Y. + * + * [Bazin et. al., 2007] A voxel v is ML-simple for R iff: + * -1 v is simple for V + * -2 v is simple for R U v + * -3 for each region O (distinct from V and R) + * that is adjacent to v: + * -a v is simple for O U V + * -b v is simple for O U R U v + * -4 for each region Q (distinct from V and R) + * that is adjacent to v: + * -a for all O, v is simple for Q U O U V + * -b for all O, v is simple for Q U O U V + * + * + * In order to test if a point is ML-simple for a given label, + * it is enough to call the `operator()` or the `isMLSimple` method: + * + * @code + + DGtal::SimplePointHelper h(anImage); + trace.info() << h.isMLsimple(aPoint, aLabel) << endl; + trace.info() << h(aPoint, aLabel) << endl; + + * @endcode + * + * @tparam TImage type of image. + */ + template + class SimplePointHelper + { + + BOOST_STATIC_ASSERT( (TImage::dimension<=3)&&(TImage::dimension>=1) ); + + // ----------------------- Types ------------------------------ + public: + + + typedef TImage LabelsImage; + typedef typename TImage::Value Label; + // Why is this concept not in DGtal::concepts namespace ? + BOOST_CONCEPT_ASSERT ((CLabel