diff --git a/examples/images/example2DBijectiveRotations.cpp b/examples/images/example2DBijectiveRotations.cpp new file mode 100644 index 0000000000..5a77fab0dc --- /dev/null +++ b/examples/images/example2DBijectiveRotations.cpp @@ -0,0 +1,147 @@ +/** +* 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 CBDR.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly ; stephane.breuils@univ-smb.fr + * @ingroup Examples + * @date 2024/08 + * + * This file is part of the DGtal library. + */ +#include +#include "DGtal/images/ImageContainerBySTLVector.h" +#include "DGtal/helpers/StdDefs.h" +#include "DGtal/base/Common.h" +#include "DGtal/io/Color.h" +#include "DGtal/io/readers/PPMReader.h" +#include "DGtal/io/writers/GenericWriter.h" +#include "DGtal/images/RigidTransformation2D.h" + + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include "DGtal/images/bijectiverotations/QSH.h" +#include "DGtal/images/bijectiverotations/CDLR.h" +#include "DGtal/images/bijectiverotations/RBC.h" +#include "DGtal/images/bijectiverotations/OTC.h" +#include "DGtal/images/bijectiverotations/CBDR.h" +#include "DGtal/images/bijectiverotations/Rotationtables.h" + + +using namespace std; +using namespace DGtal; +using namespace functors; +using namespace Z2i; + +std::vector supportedBijectiveRotation = { + "OTC", "CBDR", "CDLR", "QSH" , "RBC" +}; + +// Generic function to handle rotation based on type +template +TImage +performRotation( const TImage& img, TBijectiveRotation& obj ) +{ + const auto& sbr = supportedBijectiveRotation; + std::string structName = obj.tostring(); + TImage output( img.domain() ); + if ( std::find( sbr.begin(), sbr.end(), structName ) != sbr.end() ) + { + trace.beginBlock ( obj.tostring() ); + output = obj.rotateImage( img ); + trace.endBlock (); + } + else + throw std::runtime_error("Unsupported bijective rotation : " + structName); + return output; +} + +void usage( const std::string& cmd ) +{ + std::cout << "Usage: " << cmd << " [] [black|*white*] [detect]" << "\n" + << "\t Compute the rotated by an angle (in degrees) \n" + << "\t - in { OTC, CBDR, CDLR, QSH, *RBC* }\n" + << std::endl; +} + + +int main( int argc, char* argv[] ) +{ + if ( argc < 3 ) { usage( argv[ 0 ] ); return 1; } + std::string fname = argv[ 1 ]; + double degree = std::atof( argv[ 2 ] ); + double angle = degree * M_PI / 180.0; + std::string method = ( argc > 3 ) ? argv[ 3 ] : "RBC"; + + typedef ImageContainerBySTLVector< Domain, Color > Image; + typedef ForwardRigidTransformation2D < Space > ForwardTrans; + typedef DomainRigidTransformation2D < Domain, ForwardTrans > MyDomainTransformer; + typedef MyDomainTransformer::Bounds Bounds; + + Image image = DGtal::PPMReader::importPPM ( fname ); + Image output = image; + int W=image.domain().myUpperBound[0]+1; + int H=image.domain().myUpperBound[1]+1; + trace.info() << "Image has size " << W << "x" << H << std::endl; + Point c(W/2, H/2); + + if ( method == "QSH" ) + { + DGtal::QSH rotQSH(angle,c); + output = performRotation> (image,rotQSH); + } + else if ( method == "CDLR" ) + { + auto linf = std::make_shared>>(); + DGtal::CDLR > rotDSL(angle, c, linf); + output = performRotation> (image,rotDSL); + } + else if ( method == "RBC" ) + { + DGtal::RBC_vec rot_rbcvec(2*max(W,H)); + rot_rbcvec.setAngle() = angle; + rot_rbcvec.center() = c; + DGtal::RBC rot_RBC(rot_rbcvec,angle,c); + output = performRotation> (image,rot_RBC); + } + else if ( method == "CBDR" ) + { + auto linfCBDR = std::make_shared>>(); + const int n = 4; + const int kmax=30; + DGtal::CBDR rot_CBDR(angle,c,n,kmax,linfCBDR,true); + output = performRotation> (image,rot_CBDR); + } + + else if ( method == "OTC" ) + { + int rwidth = 2; + std::vector< std::vector< int > > tableOTC = DGtal::functions::loadOTCTable("../tables/",rwidth); + DGtal::OTC rot_OTC( tableOTC, rwidth, c, W, H ); + int alpha = int( round( angle * 180.0 / M_PI ) ); + rot_OTC.set_angle( alpha ); // in degree + output = performRotation> (image,rot_OTC); + } + + + std::string out_fname = "rotated-image-" + method + "-" + std::to_string( int(degree) ); + out_fname += ".ppm"; + output >> out_fname; + return 0; +} \ No newline at end of file diff --git a/src/DGtal/images/bijectiveRotations/CBDR.h b/src/DGtal/images/bijectiveRotations/CBDR.h new file mode 100644 index 0000000000..06c8dc7795 --- /dev/null +++ b/src/DGtal/images/bijectiveRotations/CBDR.h @@ -0,0 +1,261 @@ +/** +* 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 CBDR.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/08 + * + * This file is part of the DGtal library. + */ + +#if defined(CBDR_RECURSES) +#error Recursive header files inclusion detected in CBDR.h +#else // defined(CBDR_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define CBDR_RECURSES + +#if !defined CBDR_h +/** Prevents repeated inclusion of headers. */ +#define CBDR_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include "GAVector.h" +#include "CBDRSolver.h" +#include "CBDRFastSolver.h" + +#include "Rotationtables.h" +#include "NBijectiveReflectionGenerator.h" + + +namespace DGtal { + /** + * Description of template struct CBDR + * \brief CBDR : Composition of Bijective Digitized Reflections, + * @tparam TSpace a 2 dimensional space. + * @tparam TInputValue type of the input point e.g., TSpace::RealPoint. + * @tparam TOutputValue type of the output point e.g., TSpace::Point + */ + template +struct CBDR { + ///Checking concepts + BOOST_CONCEPT_ASSERT(( concepts::CSpace )); + BOOST_STATIC_ASSERT(( TSpace::dimension == 2 )); + BOOST_STATIC_ASSERT(( TOutputValue::dimension == 2 )); + BOOST_STATIC_ASSERT(( TInputValue::dimension == 2 )); + + + typedef Reflection DigitizedReflection; + typedef std::vector>,GAVector>> BijectiveSearchTree; + + + /** + * CBDR Constructor. + * @param theta the angle given in radians. + * @param center the center of rotation. + * @param nbreflect the number of composition of bjijective reflections applied (2,4) + * @param km conditions the number of bijective reflection normal vectors + * @param policy either Linf, L2, Lcontinuity, see Policy + * @param precompute use precomputed table of the sorted bijective composition of bijective digitized reflections + * @param fast use the table that stores for each angle the composition that minimised the Linf metric distorsion + */ + CBDR(const double theta,const typename TSpace::Point center,const size_t nbreflect,const size_t km, std::shared_ptr,CBDR_naiverotation>> policy, + const bool precompute=true, const bool fast =true ):nbijectiveGen(km),my_domain(typename TSpace::Point(0,0),typename TSpace::Point(100,100)),my_angle(theta),my_center(center),nbReflections(nbreflect),kmax(km),my_policy(policy), + usePrecomputedTable(precompute),useFastTable(fast),N(100),my_cbdr(initCBDRVec()) { + } + + + + + // whenever fast is set, load the table that stores for each angle the optimised set of bijective reflections + std::shared_ptr> initCBDRVec() { + if(useFastTable) { + std::cout << "use fast table"<> cbdrFastSolver(fastCBDRTable,my_angle,my_center,kmax); + return std::make_shared>(cbdrFastSolver.solve()); + }else{ + initcbdr_loadBijectiveReflectionSearchTree(my_domain); + /// solver part + CBDRSolver> cbdrSolver(my_angle, my_center,kmax,N); + return std::make_shared>(cbdrSolver.solve(my_domain,nbijectiveGen, vecBijectiveSearchTree,*my_policy)); + } + } + + void set_angle(const double newAngle) { + my_angle = newAngle; + if(fastTableFound) { + // does not use the policy since the table is already stored. + CBDRFastSolver> cbdrFastSolver(fastCBDRTable,my_angle,my_center,kmax); + my_cbdr=std::make_shared>(cbdrFastSolver.solve()); + }else { + CBDRSolver> cbdrSolver(my_angle, my_center,kmax,N); + my_cbdr=std::make_shared>(cbdrSolver.solve(my_domain,nbijectiveGen, vecBijectiveSearchTree,*my_policy)); + } + } + + void setPolicy(const std::shared_ptr,CBDR_naiverotation>>& newPolicy) { + // need to search again for the precomputed tables + my_policy = newPolicy; + my_cbdr = initCBDRVec(); + } + + + TOutputValue operator()( const TInputValue & aInput ) const + { + return my_cbdr->operator()(aInput-my_center)+my_center; + } + + template + TImage rotateImage(TImage img) const { + return my_cbdr->rotateImage(img); + } + + std::string tostring() const { + return {"CBDR"}; + } + + /// @return the centre of rotation + inline TOutputValue center() const{return my_center;} + + public: + NBijectiveGenerator nbijectiveGen; + std::vector vecBijectiveSearchTree; + HyperRectDomain my_domain; + + size_t kmax; + size_t nbReflections; + bool usePrecomputedTable; + bool useFastTable; + int N;/// number of sample rotation angle + double my_angle; + TOutputValue my_center; + std::shared_ptr,CBDR_naiverotation>> my_policy; + std::vector> fastCBDRTable; + std::shared_ptr> my_cbdr; + + + private: + bool fastTableFound; + + /// load fast optimised table in case the table is found + std::vector> loadFastOptimisedTable(const std::string& fastTableName); + void initcbdr_loadBijectiveReflectionSearchTree(const HyperRectDomain& my_domain); + std::vector> initFastPrecomputationTable( + const HyperRectDomain& points, + NBijectiveGenerator& nbijectiveVectors, + std::vector& vecBijectiveSearchTree); + }; + + + template + std::vector> CBDR::loadFastOptimisedTable(const std::string &fastTableName) { + auto vecTable = DGtal::functions::loadFastCBDRTable>(fastTableName); + std::vector> fastCBDRtab(vecTable.size()); + std::transform(vecTable.begin(),vecTable.end(), fastCBDRtab.begin(), [](std::tuple>>,double,double>& x) { + return DGtal::CBDR_naiverotation>(std::get<0>(x)); + }); + return fastCBDRtab; + } + + template + void CBDR::initcbdr_loadBijectiveReflectionSearchTree(const HyperRectDomain& my_domain) { + + for(size_t nbReflec = 2 ; nbReflec <= nbReflections ; nbReflec+=2) { + BijectiveSearchTree vecBijNormals; + std::string tableName("CBDRTable_"+std::to_string(nbReflec)+"_"+std::to_string(kmax)+".txt"); + // check that the file exists when usePrecomputedTable is set to true + if(usePrecomputedTable ) { + struct stat buffer; + bool exists = (stat (tableName.c_str(), &buffer) == 0); + if(!exists) { + usePrecomputedTable=false; + } + } + + if(usePrecomputedTable){ + std::cout << "loading precomputed table ..."<(tableName,nbReflec,kmax); + } else { + std::cout << "does not use precomputed table"< + std::vector> CBDR:: + initFastPrecomputationTable(const HyperRectDomain &points, NBijectiveGenerator &nbijectiveVectors, + std::vector &vecBijectiveSearchTree) { + std::vector> fastCBDRtab; + auto Linf = std::make_shared,DGtal::CBDR_naiverotation>>(); + auto LContinuity = std::make_shared,DGtal::CBDR_naiverotation>>(); + + std::string tablefilename ="CBDROptimisedTable_"+std::to_string(nbReflections)+"_"+std::to_string(kmax)+".txt"; + std::ofstream file(tablefilename); + for(int alpha = 0 ; alpha < 91; ++alpha) { + double currentAngle = (alpha * M_PI) / 180.0; + CBDRSolver> cbdrsolv(currentAngle, my_center,kmax,N); + std::vector> bestReflections =cbdrsolv.solve(points,nbijectiveVectors, + vecBijectiveSearchTree,*my_policy); + + CBDR_naiverotation currentRotation(bestReflections); + fastCBDRtab.push_back(currentRotation); + + // display vector of reflections + for(size_t i = 0 ; ievaluate(points,currentRotation,currentAngle,my_center); + file << ";"<evaluate(points,currentRotation,currentAngle,my_center); + file << std::endl; + + } + file.close(); + std::cout << "table written !"<. + * + **/ + +#pragma once + +/** +* @file CBDRFastSolver.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/08 + * + * This file is part of the DGtal library. + */ + +#if defined(CBDRFASTSOLVER_RECURSES) +#error Recursive header files inclusion detected in CBDRFastSolver.h +#else // defined(CBDRFASTSOLVER_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define CBDRFASTSOLVER_RECURSES + +#if !defined CBDRFASTSOLVER_h +/** Prevents repeated inclusion of headers. */ +#define CBDRFASTSOLVER_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include "NBijectiveReflectionGenerator.h" +namespace DGtal { + template + class CBDRFastSolver { + typedef CBDR_naiverotation BijectiveReflections; + typedef Reflection DigitizedReflection; + public: + CBDRFastSolver(const std::vector& vecOptimisedReflections,const double rotAngle, + const typename TDomain::Point center, + const int km):my_vecOptimisedReflections(vecOptimisedReflections),my_angle(rotAngle),my_center(center),kmax(km){} + + BijectiveReflections solve() { + double angleInDegrees = my_angle * 180.0 / M_PI; + angleInDegrees = fmod(angleInDegrees, 360.0); + if (angleInDegrees < 0) { + angleInDegrees += 360.0; + } + return my_vecOptimisedReflections[static_cast(std::round(angleInDegrees))]; + } + protected: + std::vector my_vecOptimisedReflections; + double my_angle; + typename TDomain::Point my_center; + int kmax; + }; +} + + + +#endif //CBDRFASTSOLVER +#undef CBDRFASTSOLVER_RECURSES +#endif // else defined(CBDRFASTSOLVER_RECURSES) \ No newline at end of file diff --git a/src/DGtal/images/bijectiveRotations/CBDRSolver.h b/src/DGtal/images/bijectiveRotations/CBDRSolver.h new file mode 100644 index 0000000000..6b2be68bdc --- /dev/null +++ b/src/DGtal/images/bijectiveRotations/CBDRSolver.h @@ -0,0 +1,178 @@ +/** +* 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 CBDRSolver.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/08 + * + * This file is part of the DGtal library. + */ + +#if defined(CBDRSOLVER_RECURSES) +#error Recursive header files inclusion detected in CBDRSolver.h +#else // defined(CBDRSOLVER_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define CBDRSOLVER_RECURSES + +#if !defined CBDRSOLVER_h +/** Prevents repeated inclusion of headers. */ +#define CBDRSOLVER_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include +#include "Policy.h" +#include "NBijectiveReflectionGenerator.h" +namespace DGtal{ + /** + * Description of template struct 'CBDR Solver' + * \brief CBDR solver, use a policy to choose the composition of digitized reflections that minimises an error + * @tparam TSpace a 2 dimensional space. + * @tparam TDomain a 2 dimensional domain. + */ + template + class CBDRSolver_GAvec{ + public: + typedef CBDR_naiverotation BijectiveReflections; + typedef functors::ForwardRigidTransformation2D RealRotation; + typedef std::vector>,GAVector>> bijectiveReflect; + typedef ErrorVectorField ErrorRealVectors; + typedef Reflection DigitizedReflection; + typedef std::vector> VectorField; + + + CBDRSolver_GAvec(const size_t km, const double rotAngle, const typename TDomain::Point center):nBijectiveGenerator(km),my_angle(rotAngle),my_center(center){} + + /// find the composition of bijective reflections that minimize either + /// Linf, L2 or Lcontinuity of error vector field given by the policy + /// @return {vector that minimizes an error given by policy, the error} + std::pair>,double> outputCompositionReflection(const TDomain& set2d, + typename bijectiveReflect::iterator& lowerAngle, + typename bijectiveReflect::iterator& upperAngle, + const Policy& policy){ + + // first error + auto it = lowerAngle; + + typename bijectiveReflect::iterator itMinError; + + std::vector bijectiveReflections; + std::vector> firstReflectionsIndex=(*lowerAngle).first; + + for(size_t i = 0 ;i reflections(bijectiveReflections); + + // finally get the average error + double minError = policy.evaluate(set2d, reflections,my_angle, my_center); //outputError(firsterrors,policy); // should now be policy + + itMinError = lowerAngle; + + for(it = lowerAngle+1 ; it != upperAngle ; ++it ){ + std::vector currentbijectiveReflections; + std::vector> currentReflectionsIndex=(*it).first; + for(size_t i = 0 ;i currentreflections(currentbijectiveReflections); + + // // finally get the error + double error = policy.evaluate(set2d, currentreflections,my_angle, my_center); + if(fabs(error) nBijectiveGenerator; + double my_angle; + typename TDomain::Point my_center; + }; + + + /** + * Description of template struct 'CBDR Solver' + * \brief Aim: CBDR solver, use a policy to choose the composition of digitized reflections that minimises an error + * @tparam TSpace a 2 dimensional space. + * @tparam TDomain a 2 dimensional domain. + */ + template + struct CBDRSolver { + typedef CBDR_naiverotation BijectiveReflections; + typedef functors::ForwardRigidTransformation2D RealRotation; + typedef std::vector>,GAVector>> bijectiveReflect; + typedef ErrorVectorField ErrorRealVectors; + typedef Reflection DigitizedReflection; + typedef std::vector> VectorField; + typedef std::vector>,GAVector>> BijectiveSearchTree; + + CBDRSolver(const double rotAngle, const typename TDomain::Point center,const int km, const int NSamples):my_angle(rotAngle),my_center(center),kmax(km),N(NSamples){} + + void setPolicy(const Policy customPolicy){my_policy(customPolicy);} + + std::vector> solve(const TDomain& points,NBijectiveGenerator& nbijectiveVectors, std::vector& vecBijectiveSearchTree,const Policy& policy){ + /// assume the sorted table was already generated either from a file or through one of the function of NbictiveGenerator + std::vector> bestParam; + double errorMin = points.myUpperBound[0]; + for(size_t nReflection = 0 ; nReflection < vecBijectiveSearchTree.size();++nReflection){ + typename std::vector>,GAVector>>::iterator lowerAngle; + typename std::vector>,GAVector>>::iterator upperAngle; + int numberOfCompositions=N; + + nbijectiveVectors.getKNearestBijectiveComposition( + vecBijectiveSearchTree[nReflection], + lowerAngle, + upperAngle, + numberOfCompositions, + my_angle); + + + CBDRSolver_GAvec rotationSolver(kmax,my_angle,my_center); + std::pair>,double> bestParam_Error =rotationSolver.outputCompositionReflection(points,lowerAngle,upperAngle,policy); + if(bestParam_Error.second < errorMin) { + bestParam = bestParam_Error.first; + errorMin = bestParam_Error.second; + } + } + std::vector bestGAVectors; + for(GAVector indexB:bestParam){ + bestGAVectors.push_back(DigitizedReflection(indexB)); + } + return bestGAVectors; + } + + + protected: + int kmax; + int N;/// number of sample rotation angle + double my_angle; + typename TDomain::Point my_center; + + }; +} + + +#endif //CBDRSOLVER +#undef CBDRSOLVER_RECURSES +#endif // else defined(CBDRSOLVER_RECURSES) + diff --git a/src/DGtal/images/bijectiveRotations/CBDR_naiverotation.h b/src/DGtal/images/bijectiveRotations/CBDR_naiverotation.h new file mode 100644 index 0000000000..ee9b01eb65 --- /dev/null +++ b/src/DGtal/images/bijectiveRotations/CBDR_naiverotation.h @@ -0,0 +1,104 @@ +/** +* 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 CBDR_naiverotation.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/08 + * + * This file is part of the DGtal library. + */ + +#if defined(CBDRNAIVEROTATION_RECURSES) +#error Recursive header files inclusion detected in CBDR_naiverotation.h +#else // defined(CBDRNAIVEROTATION_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define CBDRNAIVEROTATION_RECURSES + +#if !defined CBDRNAIVEROTATION_h +/** Prevents repeated inclusion of headers. */ +#define CBDRNAIVEROTATION_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include "DigitizedReflection.h" +#include "GAVector.h" +namespace DGtal { + /// vec since the parameters are the vectors of digitized reflections + template +struct CBDR_naiverotation{ + typedef Reflection DigitizedReflection; + + std::vector bijectiveNormalVectors; + + CBDR_naiverotation(const std::vector& bijectiveReflections={} ):bijectiveNormalVectors(bijectiveReflections){ } + + explicit CBDR_naiverotation(const std::vector>& bijectiveGAvec ){ + bijectiveNormalVectors.resize(bijectiveGAvec.size()); + std::transform(bijectiveGAvec.begin(), bijectiveGAvec.end(), bijectiveNormalVectors.begin(), + [](const GAVector& p) { return DigitizedReflection(p); }); + } + + /** + * Operator + * @return apply the composition of bijective digitized reflection to the point. + */ + TOutputValue operator()( const TInputValue & aInput ) const + { + if(bijectiveNormalVectors.size()<=0){ + return (TOutputValue)aInput; + } + int i =0; + DigitizedReflection firstReflection(bijectiveNormalVectors[i]); + TOutputValue resultat = firstReflection(aInput); + + i+=1; + for(; i < bijectiveNormalVectors.size(); ++i) + { + DigitizedReflection currentReflection(bijectiveNormalVectors[i]); + resultat = currentReflection(resultat); + } + return resultat; + } + + template + TImage rotateImage(TImage img) const { + typedef typename TImage::Domain TDomain; + typedef DGtal::functors::DomainRigidTransformation2D < typename TImage::Domain, DGtal::CBDR_naiverotation> MyDomainTransformer; + typedef std::pair < typename TSpace::Point, typename TSpace::Point > Bounds; + + MyDomainTransformer domainTransformer ( *this ); + Bounds bounds = domainTransformer ( img.domain() ); + TDomain transformedDomain ( bounds.first, bounds.second ); + TImage rotatedImage ( transformedDomain ); + + for (typename TDomain::ConstIterator it = img.domain().begin(); it != img.domain().end(); ++it ) + { + rotatedImage.setValue((*this)(*it),img(*it)); + } + return rotatedImage; + } + + }; +} + + +#endif //CBDRNAIVEROTATION +#undef CBDRNAIVEROTATION_RECURSES +#endif // else defined(CBDRNAIVEROTATION_RECURSES) diff --git a/src/DGtal/images/bijectiveRotations/CDLR.h b/src/DGtal/images/bijectiveRotations/CDLR.h new file mode 100644 index 0000000000..c9950c4112 --- /dev/null +++ b/src/DGtal/images/bijectiveRotations/CDLR.h @@ -0,0 +1,203 @@ +/** +* 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 CDLR.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/07/9 + * + * This file is part of the DGtal library. + */ + +#if defined(CDLR_RECURSES) +#error Recursive header files inclusion detected in CDLR.h +#else // defined(CDLR_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define CDLR_RECURSES + +#if !defined CDLR_h +/** Prevents repeated inclusion of headers. */ +#define CDLR_h + +#include "DGtal/base/Common.h" +#include +#include "CDLR_naiverotation.h" +#include "DigitizedReflection.h" +#include "Policy.h" +namespace DGtal { + + /** + * Description of template struct CDLR + * \brief CDLR : Rotation with Discrete Line Reflections, + * @tparam TSpace a 2 dimensional space. + * @tparam TInputValue type of the input point e.g., TSpace::RealPoint. + * @tparam TOutputValue type of the output point e.g., TSpace::Point + */ + template < typename TSpace, typename TInputValue = typename TSpace::Point, typename TOutputValue = typename TSpace::Point> + class CDLR { + public: + /** + * CDLR Constructor. + * @param ang the angle given in radians. + * @param ptCenter the center of rotation. + * @param policy either Linf, L2, Lcontinuity, see Policy + */ + CDLR( double ang, TOutputValue ptCenter, std::shared_ptr,CDLR_naiverotation>> policy ); + + /// @return the angle of rotation. + inline double angle() const{return my_angle;}; + /// @return set the angle of rotation and call the composition of reflections solver. + inline void set_angle(const double new_angle) { + my_angle=new_angle; + my_naive_rdlr_rotation.set_angle(new_angle); + dslSolver(new_angle,my_center); + } + + /// initialisation function to find the composition of Discrete Line Reflection that minimises the sum of + /// linf and lcontinuity + void dslSolver(double ang, TOutputValue ptCenter); + + + + /// @return the centre of rotation + inline TOutputValue center() const{return my_center;}; + /// @return a reference to the centre of rotation + inline TOutputValue& center(){return my_center;}; + + /// @param p a lattice point + /// @return the rotation of the point \a p according to the current + /// angle and center. + TOutputValue rotate( const TInputValue& p ) const; + TOutputValue operator()( const TInputValue& p ) const; + + template + Img rotateImage(Img img) const; + + std::string tostring() const { + return {"CDLR"}; + } + + protected: + /// The angle of rotation + double my_angle; + /// The center of rotation + TOutputValue my_center; + + + + CDLR_naiverotation my_naive_rdlr_rotation; + std::shared_ptr,CDLR_naiverotation>> my_policy; + + private: + /// DSL specific variables, see Andres paper + double a; + double b; + double my_minAngle; + }; + + template + CDLR::CDLR( const double ang, const TOutputValue ptCenter, std::shared_ptr,CDLR_naiverotation>> policy ):my_angle(ang),my_center(ptCenter),my_naive_rdlr_rotation(ang,ptCenter,0.),my_policy(policy){ + dslSolver(ang,ptCenter); + } + + template + void CDLR::dslSolver(double ang, TOutputValue ptCenter) { + + a = std::sin(ang/2.0); + b = std::cos(ang/2.0); + + TInputValue origin(0,0); + DGtal::functors::ForwardRigidTransformation2D::RealPoint,DGtal::functors::Identity> euclideanRot(my_center,my_angle,origin); + + double errorMinAlpha = 200.*200; + int N = 200; + + // create the domain + TOutputValue A(0,0); + TOutputValue B(1000,1000); + HyperRectDomain my_domain(A,B); + + + /// look for the pair of reflections that minimizes the Linf and Lcontinuity norm + std::vector mix_errors; + std::vector angles; + for(int k=0;k linf_per_image; + my_naive_rdlr_rotation.set_startingAngle(alphaCourant); + double error = my_policy->evaluate(my_domain,my_naive_rdlr_rotation,my_angle,my_center); + mix_errors.push_back(error); + } + for(int idxImages = 0 ; idxImages < mix_errors.size() ; idxImages++) { + if(mix_errors[idxImages] < errorMinAlpha) { + errorMinAlpha = mix_errors[idxImages]; + my_minAngle = angles[idxImages]; + } + } + } + + + template + TOutputValue CDLR::rotate( const TInputValue& p ) const{ + return this->operator()(p); + } + + + + + + + + template + TOutputValue CDLR::operator()( const TInputValue& p ) const{ + return my_naive_rdlr_rotation.reflect(my_minAngle+(my_angle),my_center,my_naive_rdlr_rotation.reflect(my_minAngle,my_center,p-my_center))+my_center; + } + + + template + template + TImage CDLR::rotateImage( const TImage img ) const{ + typedef typename TImage::Domain TDomain; + typedef DGtal::functors::DomainRigidTransformation2D < typename TImage::Domain, DGtal::CDLR> MyDomainTransformer; + typedef std::pair < typename TSpace::Point, typename TSpace::Point > Bounds; + + typename TSpace::Point bottomLeft(-2,-2); + typename TSpace::Point topRight(2,2); + MyDomainTransformer domainTransformer ( *this ); + Bounds bounds = domainTransformer ( img.domain() ); + TDomain transformedDomain ( bounds.first+bottomLeft, bounds.second+topRight ); + TImage rotatedImage ( transformedDomain ); + + for (typename TDomain::ConstIterator it = img.domain().begin(); it != img.domain().end(); ++it ) + { + rotatedImage.setValue((*this)(*it),img(*it)); + } + return rotatedImage; + } + + +} + + +#endif //CDLR +#undef CDLR_RECURSES +#endif // else defined(CDLR_RECURSES) diff --git a/src/DGtal/images/bijectiveRotations/CDLR_naiverotation.h b/src/DGtal/images/bijectiveRotations/CDLR_naiverotation.h new file mode 100644 index 0000000000..8aa66bc7f6 --- /dev/null +++ b/src/DGtal/images/bijectiveRotations/CDLR_naiverotation.h @@ -0,0 +1,139 @@ + +/** +* 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 CDLR_naiverotation.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/07 + * + * This file is part of the DGtal library. + */ + +#if defined(CDLR_NAIVEROTATION_RECURSES) +#error Recursive header files inclusion detected in RDSL.h +#else // defined(CDLR_NAIVEROTATION_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define CDLR_NAIVEROTATION_RECURSES + +#if !defined CDLR_NAIVEROTATION_h +/** Prevents repeated inclusion of headers. */ +#define CDLR_NAIVEROTATION_h + +#include "DGtal/base/Common.h" +#include +#include "PointUtils.h" +#include "DigitizedReflection.h" + +namespace DGtal { + + + template < typename TSpace, typename TInputValue = typename TSpace::Point, typename TOutputValue = typename TSpace::Point> + struct CDLR_naiverotation { + + inline int X(int y, const double a, const double b, const int k) const { + return std::ceil((2.0*k-1)/2.0 - (a/b)*(y)); // a = sin(theta) ; b=cos(theta) + } + + TOutputValue reflect(const double angle, TOutputValue center, const TInputValue& p ) const{ + double a = std::sin(angle/2.0); + double b = std::cos(angle/2.0); + TOutputValue pcentered = p; + + int k = std::floor(pcentered[0] + (a/b)*pcentered[1] + 0.5); + + TOutputValue X1 = TOutputValue(X(std::ceil(a*b*k),a,b,k),std::ceil(a*b*k)); + TOutputValue X2 = TOutputValue(X(std::floor(a*b*k),a,b,k),std::floor(a*b*k)); + + const double line2 = a*X1[0]-b*X1[1]; + if(line2(-b/2)){ + return TOutputValue(X(2*X1[1]-pcentered[1],a,b,k),2*X1[1]-pcentered[1]); + }else{ + const double line3 = a*X2[0]-b*X2[1]; + if(line3(-b/2)){ + return TOutputValue(X(2*X2[1]-pcentered[1],a,b,k),2*X2[1]-pcentered[1]); + } + else{ + return TOutputValue(X(X1[1]+X2[1]-pcentered[1],a,b,k),X1[1]+X2[1]-pcentered[1]); + } + } + + + } + + + + + CDLR_naiverotation( double ang=0., TOutputValue ptCenter=TOutputValue(0,0), double starting_angle =0. ):my_angle(ang),my_center(ptCenter),my_startingAngle(starting_angle) { + } + + /// @return the angle of rotation. + inline double angle() const{return my_angle;}; + + /// @return the starting angle. + inline double startingAngle() const{return my_startingAngle;}; + + ///set the angle of rotation and call the composition of reflections solver. + inline void set_angle(const double new_angle){my_angle=new_angle;} + + /// set the starting angle not the rotation angle + inline void set_startingAngle(const double new_startingAngle){my_startingAngle=new_startingAngle;} + + + + + /// @return the centre of rotation + inline TOutputValue center() const{return my_center;}; + /// @return a reference to the centre of rotation + inline TOutputValue& center(){return my_center;}; + + /// @param p a lattice point + /// @return the rotation of the point \a p according to the current + /// angle and center. + TOutputValue rotate( const TInputValue& p ) const { + return reflect(my_startingAngle + my_angle, my_center, + reflect( + my_startingAngle, my_center, p)); + } + TOutputValue operator()( const TInputValue& p ) const { + return rotate(p); + } + + + + + protected: + /// The angle of rotation. + double my_angle; + double my_startingAngle; + + /// The center of rotation. + TOutputValue my_center; + + + }; + +} + + + +#endif //CDLR_NAIVEROTATION +#undef CDLR_NAIVEROTATION_RECURSES +#endif // else defined(CDLR_NAIVEROTATION_RECURSES) + diff --git a/src/DGtal/images/bijectiveRotations/DigitizedReflection.h b/src/DGtal/images/bijectiveRotations/DigitizedReflection.h new file mode 100644 index 0000000000..af121fdc96 --- /dev/null +++ b/src/DGtal/images/bijectiveRotations/DigitizedReflection.h @@ -0,0 +1,70 @@ +/** +* 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 DigitizedReflection.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/07/9 + * + * This file is part of the DGtal library. + */ + +#if defined(DigitizedReflection_RECURSES) +#error Recursive header files inclusion detected in DigitizedReflection.h +#else // defined(DigitizedReflection_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define DigitizedReflection_RECURSES + +#if !defined DigitizedReflection_h +/** Prevents repeated inclusion of headers. */ +#define DigitizedReflection_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include "GAVector.h" + +namespace DGtal { + template + struct Reflection + { + GAVector normalVector; + + explicit Reflection ( const GAVector & m=GAVector()) + :normalVector(m){} + + /** + * Operator + * @return the reflected and digitized point. + */ + inline + TOutputValue operator()( const TInputValue & aInput ) const + { + DGtal::functors::VectorRounding < TInputValue, TOutputValue > roundingOpe; + Z2i::RealPoint m_r = Z2i::RealPoint(normalVector.my_gavec[0],normalVector.my_gavec[1]); + Z2i::RealPoint x_r = Z2i::RealPoint(aInput[0],aInput[1]); + Z2i::RealPoint p=x_r - 2.0*((x_r[0]*m_r[0] + x_r[1]*m_r[1])/(m_r[0]*m_r[0] + m_r[1]*m_r[1]))*m_r; + return roundingOpe(p); + } + + }; +} +#endif //DigitizedReflection + +#undef DigitizedReflection_RECURSES +#endif // else defined(DigitizedReflection_RECURSES) \ No newline at end of file diff --git a/src/DGtal/images/bijectiveRotations/ErrorBijectiveRotation.h b/src/DGtal/images/bijectiveRotations/ErrorBijectiveRotation.h new file mode 100644 index 0000000000..828479a826 --- /dev/null +++ b/src/DGtal/images/bijectiveRotations/ErrorBijectiveRotation.h @@ -0,0 +1,102 @@ +/** +* 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 ErrorBijectiveRotation.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/07/9 + * + * This file is part of the DGtal library. + */ + +#if defined(ErrorBijectiveRotation_RECURSES) +#error Recursive header files inclusion detected in ErrorBijectiveRotation.h +#else // defined(ErrorBijectiveRotation_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define ErrorBijectiveRotation_RECURSES + +#if !defined ErrorBijectiveRotation_h +/** Prevents repeated inclusion of headers. */ +#define ErrorBijectiveRotation_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include + +namespace DGtal{ + template + struct ErrorVectorField{ + + typedef functors::ForwardRigidTransformation2D RealRotation; + typedef std::vector> VectorField; + + protected: + TBijectiveReflections normalVectors; + typename TSpace::Point my_center; + RealRotation targetRotation; + RealRotation originCenteredRotation; + + public: + ErrorVectorField(const TBijectiveReflections& reflections,const double theta,const typename TSpace::Point center):normalVectors(reflections),targetRotation(center,theta,{0,0}),my_center(center),originCenteredRotation({0,0},theta,{0,0}) + {} + + /// compute vector of errors for each pixel between either + /// - each digitized reflections and the real reflection + /// - or the the composition of digitized reflections and the final rotation + VectorField getOutputVectorFieldFromContour(const TDomain& set2dContour, bool continuityVecField =false ){ + VectorField outVecField; + + for (typename TDomain::ConstIterator it = set2dContour.begin(); it != set2dContour.end(); ++it ) { + std::vector pixelError; + typename TSpace::Point p = *it; + + typename TSpace::Point preflections = normalVectors(p-my_center)+my_center; + typename TSpace::RealPoint protation = targetRotation(p); + + // compute the error field + typename TSpace::RealPoint error = protation - preflections ; + pixelError.push_back(error); + + // compute the eucliean rotation of the neighbors of p + if(continuityVecField) { + // for the 8-Neighbor, compute the rotation + for(int veci = -1 ; veci <2 ; ++veci) { + for(int vecj = -1 ; vecj<2 ; ++vecj) { + if(veci!=0 || vecj!=0){ + typename TSpace::RealPoint vecij_rot = originCenteredRotation({static_cast(veci),static_cast(vecj)}); + typename TSpace::RealPoint neigh_rot = protation+vecij_rot; + pixelError.push_back(neigh_rot-preflections); + } + + } + } + + } + + + outVecField.push_back(pixelError); + } + return outVecField; + } + }; +} + +#endif //ErrorBijectiveRotation +#undef ErrorBijectiveRotation_RECURSES +#endif // else defined(ErrorBijectiveRotation_RECURSES) diff --git a/src/DGtal/images/bijectiveRotations/ErrorVectorField.h b/src/DGtal/images/bijectiveRotations/ErrorVectorField.h new file mode 100644 index 0000000000..7ea4228f40 --- /dev/null +++ b/src/DGtal/images/bijectiveRotations/ErrorVectorField.h @@ -0,0 +1,110 @@ +/** +* 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 ErrorVectorField.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/08 + * + * This file is part of the DGtal library. + */ + +#if defined(ERRORVECTORFIELD_RECURSES) +#error Recursive header files inclusion detected in ErrorVectorField.h +#else // defined(ERRORVECTORFIELD_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define ERRORVECTORFIELD_RECURSES + +#if !defined ERRORVECTORFIELD_h +/** Prevents repeated inclusion of headers. */ +#define ERRORVECTORFIELD_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include + +namespace DGtal{ + template + struct ErrorVectorField{ + + typedef functors::ForwardRigidTransformation2D RealRotation; + typedef std::vector> VectorField; + + protected: + TBijectiveReflections normalVectors; + typename TSpace::Point my_center; + RealRotation targetRotation; + RealRotation originCenteredRotation; + + public: + ErrorVectorField(const TBijectiveReflections& reflections,const double theta,const typename TSpace::Point center):normalVectors(reflections),targetRotation(center,theta,{0,0}),my_center(center),originCenteredRotation({0,0},theta,{0,0}) + {} + + /// compute vector of errors for each pixel between either + /// - each digitized reflections and the real reflection + /// - or the the composition of digitized reflections and the final rotation + VectorField getOutputVectorFieldFromContour(const TDomain& set2dContour, bool continuityVecField =false ){ + VectorField outVecField; + // std::cout << "DSL output vector error : normalVectors "< +#include + +namespace DGtal { + template + struct GAVector{ + TInputValue my_gavec; + + /// @brief zero multivector + explicit GAVector( const TInputValue& pt= TInputValue(0,0)) + : my_gavec( pt ) {} + + /// @brief scalar part of the a mv, i.e. compute the dot product part of the geometric product + typename TInputValue::Component dot( GAVector other ) const + { + return my_gavec[0] * other.my_gavec[0] + my_gavec[1] * other.my_gavec[1]; + } + + /// @brief bivector part of the a mv, i.e. compute the outer product part of the geometric product + typename TInputValue::Component bivectorPart( const GAVector& other ) const + { + return my_gavec[0] * other.my_gavec[1] - my_gavec[1] * other.my_gavec[0]; + } + + /// @brief geometric product with a scalar + GAVector operator*( typename TInputValue::Component f ) const + { + return GAVector( TInputValue(my_gavec[0] * f, my_gavec[1] * f) ); + } + + /// @brief geometric product between two vectors + GAVector operator*(const GAVector& v )// geometric product + { + return GAVector( TInputValue(this->dot(v), this->bivectorPart(v)) ); + } + bool operator<( const GAVector& other ) const + { + return bivectorPart( other ) > 0; + } + + double angleToXAxis() const + { + return atan2( my_gavec[1], my_gavec[0] ); + } + + bool operator==( const GAVector& other ) const + { + return bivectorPart( other ) == 0; + } + + + }; +} + +#endif //GAVector + +#undef GAVector_RECURSES +#endif // else defined(GAVector_RECURSES) diff --git a/src/DGtal/images/bijectiveRotations/NBijectiveReflectionGenerator.h b/src/DGtal/images/bijectiveRotations/NBijectiveReflectionGenerator.h new file mode 100644 index 0000000000..3eed98e44e --- /dev/null +++ b/src/DGtal/images/bijectiveRotations/NBijectiveReflectionGenerator.h @@ -0,0 +1,290 @@ +/** +* 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 NBijectiveReflectionGenerator.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/08 + * + * This file is part of the DGtal library. + */ + +#if defined(NBIJECTIVEREFLECTIONGENERATOR_RECURSES) +#error Recursive header files inclusion detected in NBijectiveReflectionGenerator.h +#else // defined(CBDRFASTSOLVER_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define NBIJECTIVEREFLECTIONGENERATOR_RECURSES + +#if !defined NBIJECTIVEREFLECTIONGENERATOR_h +/** Prevents repeated inclusion of headers. */ +#define NBIJECTIVEREFLECTIONGENERATOR_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "GAVector.h" +#include "DigitizedReflection.h" +#include "CBDR_naiverotation.h" + +namespace DGtal{ + template + struct NBijectiveGenerator{ + + explicit NBijectiveGenerator(const size_t km):kmax(km){ + for(int k=0; k({x_kpp_k,y_kpp_k})); + BijectiveVectors.push_back(GAVector({x_k_kpp,y_k_kpp})); + BijectiveVectors.push_back(GAVector({x_1_2kpp,y_1_2kpp})); + BijectiveVectors.push_back(GAVector({x_2kpp_1,y_2kpp_1})); + BijectiveVectors.push_back(GAVector({x_kpp_k_m,y_kpp_k_m})); + BijectiveVectors.push_back(GAVector({x_k_kpp_m,y_k_kpp_m})); + BijectiveVectors.push_back(GAVector({x_1_2kpp_m,y_1_2kpp_m})); + BijectiveVectors.push_back(GAVector({x_2kpp_1_m,y_2kpp_1_m})); + } + } + + + + /// @brief compose bijective digitized reflections + /// @param Avector the composition of bijective reflection + /// @return vector of indices of bijective digitized reflections + std::vector,GAVector>> composeBijectiveReflections( + std::vector,GAVector>>& Avector ){ + std::vector,GAVector>> result; + result.reserve(Avector.size()*BijectiveVectors.size()); + + for(std::pair,GAVector> normalVectorsAndResult : Avector){ + std::pair,GAVector> resultatCourant = normalVectorsAndResult; + for(size_t indexB = 0 ; indexB outVec = normalVectorsAndResult.second*BijectiveVectors[indexB]; + if(outVec.my_gavec[0] <0){ + // resultatCourant.first.insert(resultatCourant.first.begin(),indexB); + // resultatCourant.second = outVec*-1; + }else{ + resultatCourant.first.push_back(indexB); + resultatCourant.second = outVec; + result.push_back(resultatCourant); + resultatCourant=normalVectorsAndResult; + } + + } + } + // unique + std::sort(result.begin(), result.end(), [](const std::pair,GAVector>& b1, const std::pair,GAVector>& b2) { + return b1.second < b2.second;}); + + + return result; + } + + /// @brief predicate to compare two composition of bijective reflections + /// @param b1 first pair of composition of bijective reflections + /// @param b2 second pair of composition of bijective reflections + /// @return composition leads to same error? + bool sameBijectiveComposition(const std::pair,GAVector>& b1, const std::pair,GAVector>& b2) const{ + // both reflections thanks to reflection composer + if(fabs(b1.second.angleToXAxis()-b1.second.angleToXAxis())>1e-4){ + return false; + } + std::vector> normals1 = getGAVectorFromIndex(b1.first); + std::vector> normals2 = getGAVectorFromIndex(b2.first); + CBDR_naiverotation reflectionsCompose1(normals1); + CBDR_naiverotation reflectionsCompose2(normals2); + + typename TSpace::Point zeroPt(0,0); + + for(int i =0 ; i<100;++i){ + for(int j =0 ; j<100;++j){ + double x=i-50; + double y=j-50; + typename TSpace::RealPoint pointCourant = {x,y}; + typename TSpace::Point out1 = reflectionsCompose1(pointCourant); + typename TSpace::Point out2 = reflectionsCompose2(pointCourant); + if((out1-out2)!= zeroPt ){ + return false; + } + } + } + return true; + } + + + std::vector>,GAVector>> vecBijNormals_index_2_GAVector(const std::vector,GAVector>>& vecBijNormalsIndex) { + std::vector>,GAVector>> vecBijNormalsIndex_GAVector; + for(std::pair,GAVector> pairIndexGAVector : vecBijNormalsIndex) { + std::vector> resGavec; + + for(int index : pairIndexGAVector.first) { + resGavec.push_back(BijectiveVectors[index]); + } + vecBijNormalsIndex_GAVector.push_back({resGavec,pairIndexGAVector.second}); + } + return vecBijNormalsIndex_GAVector; + + } + + /// @brief generate all n =2,4 reflection composition + /// @param n number of reflection to compose + std::vector,GAVector>> n_bijectiveReflections_get_NormalVectorsAngles(size_t n) + { + std::vector,GAVector>> vecBijNormals; + for(int i =0 ; i< BijectiveVectors.size() ; ++i){ + vecBijNormals.push_back({{i},BijectiveVectors[i]}); + } + if(n>3) { + --n; + } + + for ( size_t j = 1; j < n; ++j ) + { + vecBijNormals = composeBijectiveReflections( vecBijNormals); + + // remove duplicates with predicate defined below + auto last = std::unique(vecBijNormals.begin(),vecBijNormals.end(),[this](const std::pair,GAVector>& b1, const std::pair,GAVector>& b2) { + return sameBijectiveComposition(b1, b2);}); + vecBijNormals.erase(last,vecBijNormals.end()); + } + if(n==3){ + /// interpret as the composition of 4 vectors : 3 bijective and 1 trivial bijective reflection + for(auto& pairIndicesGAVec : vecBijNormals) + pairIndicesGAVec.first.insert(pairIndicesGAVec.first.begin(),0); + } + + std::sort(vecBijNormals.begin(), vecBijNormals.end(), [](const std::pair,GAVector>& b1, const std::pair,GAVector>& b2) { + return b1.second.angleToXAxis() < b2.second.angleToXAxis();}); + + + return vecBijNormals; + } + + void displayNormalVectorsAndAngles(const std::vector,GAVector>>& vecBijNormals){ + for(size_t i =0;i finalNormalVec = vecBijNormals[i].second; + + if(2.0*finalNormalVec.angleToXAxis() < M_PI_4/8){ + std::cout << "m=["; + for(int indexBijective : currentIndicesNormalVector){ + std::cout <<"("<>,GAVector>>& vecBijNormals, + typename std::vector>,GAVector>>::iterator& lowerBound, + typename std::vector>,GAVector>>::iterator& upperBound, + const int K, + const double targetAngle) + { + // find the index of the closest angle + int i = std::lower_bound(vecBijNormals.begin(), vecBijNormals.end(), targetAngle,[](const std::pair>,GAVector>& b1, const double b2) { + return 2.0*b1.second.angleToXAxis() < b2; + }) - vecBijNormals.begin(); + + int leftCompo=i-1; + int rightCompo = i; + + // assume that K fabs(2*vecBijNormals[rightCompo].second.angleToXAxis() - targetAngle))) { + rightCompo++; + } + else { + leftCompo--; + } + numberElements++; + } + lowerBound=vecBijNormals.begin()+leftCompo; + upperBound=vecBijNormals.begin()+rightCompo; + + } + + public: + size_t kmax; + std::vector> BijectiveVectors; + }; + +} + + + +#endif //NBIJECTIVEREFLECTIONGENERATOR +#undef NBIJECTIVEREFLECTIONGENERATOR_RECURSES +#endif // else defined(NBIJECTIVEREFLECTIONGENERATOR_RECURSES) \ No newline at end of file diff --git a/src/DGtal/images/bijectiveRotations/OTC.h b/src/DGtal/images/bijectiveRotations/OTC.h new file mode 100644 index 0000000000..e0839924e4 --- /dev/null +++ b/src/DGtal/images/bijectiveRotations/OTC.h @@ -0,0 +1,211 @@ +/** +* 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 OTC.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/07/9 + * + * This file is part of the DGtal library. + */ + +#if defined(OTC_RECURSES) +#error Recursive header files inclusion detected in GAVector.h +#else // defined(OTC_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define OTC_RECURSES + +#if !defined OTC_h +/** Prevents repeated inclusion of headers. */ +#define OTC_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include +#include "RBC.h" + +namespace DGtal { + /** + * Description of template struct OTC + * \brief OTC : Optimal Transport through Circle bijective rotation + * @tparam TSpace a 2 dimensional space. + * @tparam TInputValue type of the input point e.g., TSpace::RealPoint. + * @tparam TOutputValue type of the output point e.g., TSpace::Point + */ + template +struct OTC { + const std::vector< std::vector< int > >& _table; + int _dr; + TOutputValue my_center; + int max_radius; + RBC_vec rbc; + int angle; + int _W; + int _H; + int _outS; + std::vector< int > _offset; // precomputes the number of points in circles < k + + /** + * OTC Constructor. + * @param table precomputed table thanks to the OT implementation + * @param dr width of each ring (OTC-2, OTC3) + * @param c center of rotation + * @param W,H size of the image + */ + OTC( const std::vector< std::vector< int > >& table, + int dr, + TOutputValue c, int W, int H ) + : _table( table ), rbc( 0 ) + { + _W = W; + _H = H; + _dr = dr; + TOutputValue corner00( 0, 0 ); + TOutputValue cornerW0( W-1, 0 ); + TOutputValue corner0H( 0, H-1 ); + TOutputValue cornerWH( W-1, H-1 ); + int max_radius = int( ceil( sqrt( distance2( c, corner00 ) ) ) ); + + max_radius = std::max( max_radius, int( ceil( sqrt( distance2( c, cornerW0 ) ) ) ) ); + max_radius = std::max( max_radius, int( ceil( sqrt( distance2( c, corner0H ) ) ) ) ); + max_radius = std::max( max_radius, int( ceil( sqrt( distance2( c, cornerWH ) ) ) ) ); + rbc.init( max_radius, false ); + my_center = c; + rbc.center() = c; + _outS = 2*max_radius+1; + + // Precompute offset table + _offset.resize( max_radius ); + int n = 0; + for ( auto r = 0; r < max_radius; r++ ) + { + _offset[ r ] = n; + n += rbc.circle( r ).size(); + } + } + + + /// @return the centre of rotation + inline TOutputValue center() const{return my_center;} + + + void set_angle( double alpha ) + { + angle = std::round((alpha*180.0)/M_PI); + rbc.setAngle() = alpha; + std::cout <<"OTC angle="<(p - my_center); + + //std::cout <<"pc0="<. + * + **/ + +#pragma once + +/** +* @file PointUtils.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/07/9 + * + * This file is part of the DGtal library. + */ + +#if defined(PointUtils_RECURSES) +#error Recursive header files inclusion detected in PointUtils.h +#else // defined(PointUtils_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define PointUtils_RECURSES + +#if !defined PointUtils_h +/** Prevents repeated inclusion of headers. */ +#define PointUtils_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include +#include "DGtal/base/Common.h" + +namespace DGtal{ + /// @return the determinant of p and q, seen as 2D vectors + template + typename TPointType::Coordinate det( const TPointType& p, const TPointType& q ) { + return p[ 0 ] * q[ 1 ] - p[ 1 ] * q[ 0 ]; + } + + /// Defines an order between points, which is + /// p < q <=> less( p, q ) <=> det( p, q ) > 0 + template + bool less( const TPointType& p, const TPointType& q ) { + return ( p[ 0 ] * q[ 1 ] - p[ 1 ] * q[ 0 ] ) > 0; + } + + + + /// Defines an order between points, which is + /// p < q <=> less( p, q ) <=> det( p, q ) > 0 + template + bool less( const TPointType& p, const TPointType& q ); + + /// @param p any point + /// @return the points (q_i) that are 8-connected to p and such that + /// `less( p, q_i)` + template + std::vector< TPointType > nextNeighbors( TPointType p ) { + std::vector< TPointType > V; + TPointType zero( 0, 0 ); + TPointType p0( p[ 0 ]+1, p[ 1 ] ); + if ( less( p, p0 ) ) V.push_back( p0 ); + TPointType p1( p[ 0 ]+1, p[ 1 ]+1 ); + if ( less( p, p1 ) ) V.push_back( p1 ); + TPointType p2( p[ 0 ], p[ 1 ]+1 ); + if ( less( p, p2 ) ) V.push_back( p2 ); + TPointType p3( p[ 0 ]-1, p[ 1 ]+1 ); + if ( less( p, p3 ) ) V.push_back( p3 ); + TPointType p4( p[ 0 ]-1, p[ 1 ] ); + if ( less( p, p4 ) ) V.push_back( p4 ); + TPointType p5( p[ 0 ]-1, p[ 1 ]-1 ); + if ( less( p, p5 ) ) V.push_back( p5 ); + TPointType p6( p[ 0 ], p[ 1 ]-1 ); + if ( less( p, p6 ) ) V.push_back( p6 ); + TPointType p7( p[ 0 ]+1, p[ 1 ]-1 ); + if ( less( p, p7 ) ) V.push_back( p7 ); + return V; + } + + /// @return the squared distance between points p and q + template + typename TPointType::Coordinate distance2( TPointType p, TPointType q ) { + typename TPointType::Coordinate d2 = (p[ 0 ] - q[ 0 ])*(p[ 0 ] - q[ 0 ])+(p[ 1 ] - q[ 1 ])*(p[ 1 ] - q[ 1 ]); + return d2; + } + + + + /// @return the squared distance between points p and q + template + typename TPointType2::Coordinate distance2( TPointType1 p, TPointType2 q ) { + typename TPointType2::Coordinate d2 = (p[ 0 ] - q[ 0 ])*(p[ 0 ] - q[ 0 ])+(p[ 1 ] - q[ 1 ])*(p[ 1 ] - q[ 1 ]); + return d2; + } + + +} +#endif //PointUtils +#undef PointUtils_RECURSES +#endif // else defined(PointUtils_RECURSES) \ No newline at end of file diff --git a/src/DGtal/images/bijectiveRotations/Policy.h b/src/DGtal/images/bijectiveRotations/Policy.h new file mode 100644 index 0000000000..24d12a0261 --- /dev/null +++ b/src/DGtal/images/bijectiveRotations/Policy.h @@ -0,0 +1,146 @@ +/** +* 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 Policy.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/08 + * + * This file is part of the DGtal library. + */ + +#if defined(POLICY_RECURSES) +#error Recursive header files inclusion detected in Policy.h +#else // defined(POLICY_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define POLICY_RECURSES + +#if !defined POLICY_h +/** Prevents repeated inclusion of headers. */ +#define POLICY_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions + + + +// Base Policy class +#include +#include "ErrorVectorField.h" + +namespace DGtal { + /** + * Description of template struct Policy + * \brief Policy : , + * @tparam TSpace a 2 dimensional space. + * @tparam BijectiveRotation either CDLR or CBDR struct + */ + template + struct Policy { + typedef std::vector> VectorField; + + virtual ~Policy() = default; + virtual double evaluate(const TDomain& set2d, const BijectiveRotation& reflections,double my_angle, typename TSpace::Point my_center) const = 0; + }; + + + // Linf Policy class + template + struct LinfPolicy : public Policy { + typedef std::vector> VectorField; + typedef ErrorVectorField ErrorRealVectors; + + double evaluate(const TDomain& set2d, const TBijectiveRotation& reflections,double my_angle, typename TSpace::Point my_center) const override { + ErrorRealVectors errorsVectors(reflections,my_angle,my_center); + + VectorField errors = errorsVectors.getOutputVectorFieldFromContour(set2d); + double outError = 0.; + for(std::vector vecError: errors) { + typename TSpace::RealPoint vecErrorRealRot = vecError[0]; + outError= std::max(outError,std::sqrt((vecErrorRealRot[0]*vecErrorRealRot[0]+vecErrorRealRot[1]*vecErrorRealRot[1]))); + } + return outError; + } + }; + + // Linf Policy class + template + struct L2 : public Policy { + typedef std::vector> VectorField; + typedef ErrorVectorField ErrorRealVectors; + + double evaluate(const TDomain& set2d, const TBijectiveRotation& reflections,double my_angle, typename TSpace::Point my_center) const override { + ErrorRealVectors errorsVectors(reflections,my_angle,my_center); + VectorField errors = errorsVectors.getOutputVectorFieldFromContour(set2d); + double outError = 0.; + for(std::vector vecError: errors) { + typename TSpace::RealPoint vecErrorRealRot = vecError[0]; + outError+= (vecErrorRealRot[0]*vecErrorRealRot[0]+vecErrorRealRot[1]*vecErrorRealRot[1]); + } + return std::sqrt(outError); + } + }; + + + + + // Lcontinuity Policy struct + template + struct LcontinuityPolicy : public Policy { + typedef std::vector> VectorField; + typedef ErrorVectorField ErrorRealVectors; + + double evaluate(const TDomain& set2d, const TBijectiveRotation& reflections,double my_angle, typename TSpace::Point my_center) const override { + ErrorRealVectors errorsVectors(reflections,my_angle,my_center); + VectorField errors = errorsVectors.getOutputVectorFieldFromContour(set2d,true); + double outError = 0.; + + for(std::vector vecError: errors) { + for(int i = 1 ; i +QSH::QSH( const double ang, const TOutputValue ptCenter ):my_angle(ang),my_center(ptCenter){ + initQSHRotation(); +} + + + +template +TOutputValue QSH::rotate( const TInputValue& p ) const{ + return this->operator()(p); +} + +template +TOutputValue QSH::operator()( const TInputValue& p ) const{ + + TOutputValue pcentered = p-my_center; + + TOutputValue y1 = hqs_p(pcentered, aprime, bprime, omega); + TOutputValue y2 = vqs_p(y1, a, bprime, omega); + TOutputValue y = hqs_p(y2, aprime, bprime, omega); + return y+my_center; +} + + + +template +template +TImage QSH::rotateImage( const TImage img ) const{ + typedef typename TImage::Domain TDomain; + typedef DGtal::functors::DomainRigidTransformation2D < typename TImage::Domain, DGtal::QSH> MyDomainTransformer; + typedef std::pair < typename TSpace::Point, typename TSpace::Point > Bounds; + + + MyDomainTransformer domainTransformer ( *this ); + Bounds bounds = domainTransformer ( img.domain() ); + TDomain transformedDomain ( bounds.first, bounds.second ); + TImage rotatedImage ( transformedDomain ); + + + + for (typename TDomain::ConstIterator it = img.domain().begin(); it != img.domain().end(); ++it ) + { + rotatedImage.setValue((*this)(*it),img(*it)); + } + + return rotatedImage; +} + +} + + +#endif //QSH + +#undef QSH_RECURSES +#endif // else defined(QSH_RECURSES) \ No newline at end of file diff --git a/src/DGtal/images/bijectiveRotations/RBC.h b/src/DGtal/images/bijectiveRotations/RBC.h new file mode 100644 index 0000000000..0e17016d9e --- /dev/null +++ b/src/DGtal/images/bijectiveRotations/RBC.h @@ -0,0 +1,124 @@ + +/** +* 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 RBC.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/07/9 + * + * This file is part of the DGtal library. + */ + +#if defined(RBC_RECURSES) +#error Recursive header files inclusion detected in RBC.h +#else // defined(RBC_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define RBC_RECURSES + +#if !defined RBC_h +/** Prevents repeated inclusion of headers. */ +#define RBC_h + +#include +#include +#include +#include +#include "RBC_vec.h" + +namespace DGtal { + + /** + * Description of template struct RBC + * \brief RBC : Bijective Rotation through Circles + * @tparam TSpace a 2 dimensional space. + * @tparam TInputValue type of the input point e.g., TSpace::RealPoint. + * @tparam TOutputValue type of the output point e.g., TSpace::Point + */ + template + struct RBC { + RBC_vec rot; + typedef typename RBC_vec::Circle Circle; + + /** + * RBC Constructor. + * @param angle the angle given in radians. + * @param center the center of rotation. + * @param aRot RBC Circles initialiser + */ + RBC( const RBC_vec& aRot, const double angle, const TOutputValue center ) + : rot( aRot ),my_angle(angle),my_center(center) {} + + /// Rotates the whole image \a Image circle by circle. + template + TImage rotateImage( const TImage& img) const + { + typedef typename TImage::Domain TDomain; + typedef DGtal::functors::DomainRigidTransformation2D < typename TImage::Domain, DGtal::RBC_vec> MyDomainTransformer; + typedef std::pair < typename TSpace::Point, typename TSpace::Point > Bounds; + + typename TSpace::Point bottomLeft(-1,-1); + typename TSpace::Point topRight(1,1); + + MyDomainTransformer domainTransformer ( rot ); + Bounds bounds = domainTransformer ( img.domain() ); + TDomain transformedDomain ( bounds.first+bottomLeft, bounds.second+topRight ); + TImage rotatedImage ( transformedDomain ); + + + + // for ( auto r = 1; r < rot.size(); r++ ) + // rotateCircle( img, rotatedImage, my_center, my_angle, r ); + + for (typename TDomain::ConstIterator it = img.domain().begin(); it != img.domain().end(); ++it ) { + rotatedImage.setValue((*this).rot.operator()(*it),img(*it)); + } + return rotatedImage; + } + + TOutputValue operator()( const TInputValue & aInput ) const + { + return (*this).rot.operator()(aInput); + } + + std::string tostring() const { + return {"RBC"}; + } + + void set_angle(const double newAngle) { + my_angle=newAngle; + rot.setAngle()= newAngle; + } + + /// @return the centre of rotation + inline TOutputValue center() const{return my_center;} + + + double my_angle; + TOutputValue my_center; + + }; + + +} + +#endif //RBC + +#undef RBC_RECURSES +#endif // else defined(RBC_RECURSES) \ No newline at end of file diff --git a/src/DGtal/images/bijectiveRotations/RBC_vec.h b/src/DGtal/images/bijectiveRotations/RBC_vec.h new file mode 100644 index 0000000000..002098071b --- /dev/null +++ b/src/DGtal/images/bijectiveRotations/RBC_vec.h @@ -0,0 +1,228 @@ +/** +* 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 RBC_vec.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/07/9 + * + * This file is part of the DGtal library. + */ + +#if defined(RBC_vec_RECURSES) +#error Recursive header files inclusion detected in RBC_vec.h +#else // defined(RBC_vec_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define RBC_vec_RECURSES + +#if !defined RBC_vec_h +/** Prevents repeated inclusion of headers. */ +#define RBC_vec_h + +#include +#include "DGtal/base/Common.h" +#include "DGtal/kernel/BasicPointFunctors.h" +#include +#include "PointUtils.h" +#include +#include +#include + +namespace DGtal { + /** + * Description of template struct RBC_vec + * \brief RBC : Bijective Rotation through Circles + * @tparam TSpace a 2 dimensional space. + * @tparam TInputValue type of the input point e.g., TSpace::RealPoint. + * @tparam TOutputValue type of the output point e.g., TSpace::Point + */ + template +struct RBC_vec { + typedef std::vector< TOutputValue > Circle; + + /// the array of circles, where index is the integer radius of the + /// circle. + std::vector< Circle > circles; + /// the map associating to a lattice point its polar coordinates + /// (r,idx) in the circles. + std::map< TOutputValue, TOutputValue > point2circle; + + /// Constructor + /// @param max_radius the maximal distance of a point to + /// the center of rotation. + /// @param smart when 'true', tries to regularize the number of + /// points of each circle, when 'false' each circle contains the + /// point between distance r (included) and r+1 (excluded). + RBC_vec( int max_radius, bool smart = false ) { + init( max_radius, smart ); + my_angle = 0.0; + my_center = TOutputValue( 0, 0 ); + } + + /// Initialization + /// @param R the maximal distance of a point to + /// the center of rotation. + /// + /// @param smart when 'true', tries to regularize the number of + /// points of each circle, when 'false' each circle contains the + /// point between distance r (included) and r+1 (excluded). + void init( int R, bool smart ) { + TOutputValue c( 0, 0 ); + circles.clear(); + circles.resize( R + 1 ); + circles[ 0 ].push_back( c ); + point2circle[ c ] = TOutputValue( 0, 0 ); + std::size_t N = 1; + std::vector< TOutputValue > points; + for ( int r = 1; r <= R; r++ ) + { + circles[ r ] = computeCircle( r ); + for ( int i = 0; i < circles[ r ].size(); i++, N++ ) + { + point2circle[ circles[ r ][ i ] ] = TOutputValue( r, i ); + points.push_back( circles[ r ][ i ] ); + } + } + if ( ! smart ) return; + struct DistanceComparator { + bool operator()( TOutputValue p, TOutputValue q ) const + { + return p.squaredNorm() < q.squaredNorm(); + } + }; + struct AngleComparator { + bool operator()( TOutputValue p, TOutputValue q ) const + { + double ap = atan2( p[ 1 ], p[ 0 ] ); + double aq = atan2( q[ 1 ], q[ 0 ] ); + return ap < aq; + } + }; + + // Try to improve circles. + // target number of points for circle r is + // n(r) = a * r, with a = 2( N - 1 ) / (R(R+1)) + double a = 2.0 * double( N - 1 ) / double(R*(R+1)); + DistanceComparator dcomp; + AngleComparator acomp; + std::sort( points.begin(), points.end(), dcomp ); + int current = 1; + for ( int r = 1; r <= R; r++ ) + { + int n = int( round( a * r / 8.0 ) * 8.0 ); + circles[ r ].resize( n ); + for ( int i = 0; i < n; i++ ) + circles[ r ][ i ] = points[ current++ ]; + std::sort( circles[ r ].begin(), circles[ r ].end(), acomp ); + // std::cout << "r=" << r << " #C=" << circles[ r ].size() << std::endl; + } + } + + /// @return the angle of rotation. + double angle() const { + return my_angle; + } + /// @return a reference to the angle of rotation. + double& setAngle() { + return my_angle; + } + /// @return the centre of rotation + TOutputValue center() const { + return my_center; + } + /// @return a reference to the centre of rotation + TOutputValue& center() { + return my_center; + } + + /// @param p a lattice point + /// @return the rotation of the point \a p according to the current + /// angle and center. + TOutputValue rotate( TInputValue p ) const { + auto cp = static_cast((p - my_center)); + const auto it = point2circle.find( cp ); + if ( it == point2circle.end() ) return static_cast(p); + TOutputValue polar = it->second; + int r = polar[ 0 ]; + int idx = polar[ 1 ]; + const Circle& C = circle( r ); + const int N = C.size(); + int shift = int( round( -my_angle * N / ( 2.0 * M_PI ) ) ); + int jdx = ( N + idx - shift ) % N; + return my_center + C[ jdx ]; + } + + TOutputValue operator()( const TInputValue & aInput ) const + { + return this->rotate(aInput); + } + + /// @return the number of circles + std::size_t size() const { + return circles.size(); + } + + /// @param r an integer smaller than `nbCircles()`. + /// @return the number of circles + const Circle& circle( int r ) const { + return circles[ r ]; + } + + /// @param r an integer specifying the digital radius of the circle. + /// @return the points lying in the circle between radius r + /// (included) and r+1 (excluded) + static Circle computeCircle( int r ) { + int d2_lo = r*r; + int d2_up = (r+1)*(r+1); + TOutputValue start( r, 0 ); + Circle circle; + TOutputValue current = start; + do { + circle.push_back( current ); + auto V = nextNeighbors( current ); + std::vector P; + for ( auto p : V ) { + int d2 = p.squaredNorm(); + // std::cout << "p=" << p << " d2=" << d2 << std::endl; + if ( d2_lo <= d2 && d2 < d2_up ) P.push_back( p ); + } + if ( P.empty() ) std::cerr << "Error ! " << std::endl; + TOutputValue b = P[ 0 ]; + for ( int i = 1; i < P.size(); i++ ) + if ( less( P[ i ], b ) ) b = P[ i ]; + current = b; + } while ( current != start ); + return circle; + } + + + + protected: + /// The angle of rotation. + double my_angle; + /// The center of rotation. + TOutputValue my_center; + }; +} + + +#endif //RBC_vec + +#undef RBC_vec_RECURSES +#endif // else defined(RBC_vec_RECURSES) diff --git a/src/DGtal/images/bijectiveRotations/Rotationtables.h b/src/DGtal/images/bijectiveRotations/Rotationtables.h new file mode 100755 index 0000000000..c344d2ebb4 --- /dev/null +++ b/src/DGtal/images/bijectiveRotations/Rotationtables.h @@ -0,0 +1,282 @@ + +/** +* 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 Rotationtables.h + * @author S. Breuils, J.O. Lachaud, D. Coeurjolly + * + * @date 2024/08 + * + * This file is part of the DGtal library. + */ + +#if defined(ROTATIONTABLES_RECURSES) +#error Recursive header files inclusion detected in Rotationtables.h +#else // defined(ROTATIONTABLES_RECURSES) +/** Prevents recursive inclusion of headers. */ +#define ROTATIONTABLES_RECURSES + +#if !defined ROTATIONTABLES_h +/** Prevents repeated inclusion of headers. */ +#define ROTATIONTABLES_h + +////////////////////////////////////////////////////////////////////////////// +// Inclusions +#include "GAVector.h" +#include +#include +#include +#include +#include +#include +#include +namespace DGtal { + namespace functions { + /** + * \brief Rotation table loading : for OTC and RBDR + * @tparam TSpace a 2 dimensional space. + * @tparam TInputValue type of the input point e.g., TSpace::RealPoint. + */ + template + std::vector> loadOTCTable(const std::string& tableFolderPath,const int rwidth) { + std::ostringstream ot; + ot << tableFolderPath <<"OT-" << rwidth << "-circles-l2.txt"; + std::string tname = ot.str(); + std::vector< int> Csize; + std::vector< std::vector< int > > table; + std::ifstream tinput( tname.c_str() ); + if(tinput.fail()) { + std::cerr << "OTC file not found" <> t ) Csize.push_back( t ); + // std::cout << "Read #C=" << Csize.size() << std::endl; + size_mode = false; + } else { + std::istringstream is( str ); + std::vector< int > mapping; + is >> alpha; + int ra = int( round( alpha*180.0/M_PI ) ); + if ( a == ra ) nb_ok += 1; + mapping.push_back( 0 ); // add 0 circle + while ( is >> t ) mapping.push_back( t ); + // std::cout << " #M=" << mapping.size() << std::endl; + table.push_back( mapping ); + a += 1; + } + } + } + tinput.close(); + + return table; + } + + + template + std::vector parselineWithSeparator(const std::string& coordinatesStr,const char delimiter) { + std::vector substrings; + std::istringstream iss(coordinatesStr); + + std::string substring; + while(std::getline(iss,substring,delimiter)) { + std::cout << "substring="< + std::vector> parseCoordinates(const std::string& coordinatesStr) { + int x, y; + std::istringstream iss(coordinatesStr); + char delim; + std::vector> gavecs; + + iss >> delim; + // Parse (x1,y1),(x2,y2),...,(xn,yn), n is even + while (iss >> x >> delim >> y) { + gavecs.push_back(GAVector({x, y})); + // std::cout << "x="<> delim >> delim>> delim; + } else { + break; + } + } + return gavecs; + } + + template + std::vector>,double,double>> loadFastCBDRTable(std::string fileStr) { + const char* filename =fileStr.c_str(); + std::cout << "filename="<(mmap(NULL, fileInfo.st_size, PROT_READ, MAP_PRIVATE, fileDesc, 0)); + if (fileData == MAP_FAILED) { + std::cerr << "Error mapping file to memory: " << strerror(errno) << std::endl; + close(fileDesc); + return {}; + } + + // Read lines from the memory-mapped + size_t start = 0; + + std::vector>,double,double>> vecCompositionBijective_With_Errors; + std::cout << "file info size="< + std::vector>,GAVector>> loadBijectiveRotationTable(std::string fileStr, const size_t n, const size_t kmax) { + //std::string fileStr = (("../precomputedTables/"+(std::to_string(n))+"reflectionsUnique_kmax_"+std::to_string(kmax)+".txt")); //"../precomputedTables/2reflectionsUnique_kmax_15.txt";// + const char* filename =fileStr.c_str(); + + // Open bijective file + int fileDesc = open(filename, O_RDONLY); + if (fileDesc == -1) { + std::cerr << "Error opening file: " << strerror(errno) << std::endl; + return {}; + } + + // Get the size of the file + struct stat fileInfo; + if (fstat(fileDesc, &fileInfo) == -1) { + std::cerr << "file information error : " << strerror(errno) << std::endl; + close(fileDesc); + return {}; + } + + // Map the file into memory (file size can be ~200MB for kmax=30, n=4) + char* fileData = static_cast(mmap(NULL, fileInfo.st_size, PROT_READ, MAP_PRIVATE, fileDesc, 0)); + if (fileData == MAP_FAILED) { + std::cerr << "Error mapping file to memory: " << strerror(errno) << std::endl; + close(fileDesc); + return {}; + } + + // Read lines from the memory-mapped + size_t start = 0; + bool isFirstLine = true; + int kmaxFile=0; + + std::vector>,GAVector>> vecCompositionBijective; + for (size_t i = 0; i < fileInfo.st_size; ++i) { + if (fileData[i] == '\n' || i == fileInfo.st_size - 1) { + // Extract coordinates from the line + std::string line(fileData + start, i - start + 1); + if (isFirstLine) { + size_t pos = line.find("kmax="); + if (pos != std::string::npos) { + if (sscanf(line.c_str() + pos + 5, "%d", &kmaxFile) == 1) { + std::cout << "kmax: " << kmaxFile << std::endl; + } + } + isFirstLine = false; + } else { + std::vector> coordinates; + + // Parse coordinates from the line + std::string coordinateStr; + coordinates = functions::parseCoordinates(line); + // Store the last coordinate separately + GAVector last = coordinates.back(); + coordinates.pop_back(); // Exclude the last coordinate from the main vector + vecCompositionBijective.push_back(std::make_pair(coordinates,last)); + } + start = i + 1; + } + } + + munmap(fileData, fileInfo.st_size); + close(fileDesc); + std::cout << "load done .."<. + * + **/ + +/** + * @file + * @ingroup Tests + * @author Stephane Breuils, David Coeurjolly, Jacques-Olivier Lachaud + * @date 2024/08 + * + * This file is part of the DGtal library + */ + +/** + * Description of test_BijectiveRotation

+ * Aim: simple test of Bijective rotation methods (CBDR,RDSL,QSH,OTC,RBC) + */ + +#include +#include +#include + +#include +#include +#include "DGtal/images/ImageSelector.h" +#include "DGtal/images/ImageContainerBySTLVector.h" +#include "DGtal/images/ConstImageAdapter.h" +#include "DGtal/helpers/StdDefs.h" +#include "DGtal/base/Common.h" +#include "DGtal/io/readers/PGMReader.h" +#include "DGtal/io/writers/GenericWriter.h" +//! [include] +#include "DGtal/images/RigidTransformation2D.h" +//! [include] +////////////////// +/// +#include "DGtal/images/bijectiverotations/QSH.h" +#include "DGtal/images/bijectiverotations/CDLR.h" +#include "DGtal/images/bijectiverotations/RBC.h" +#include "DGtal/images/bijectiverotations/OTC.h" +#include "DGtal/images/bijectiverotations/CBDR.h" +#include "DGtal/images/bijectiverotations/Rotationtables.h" + + +using namespace std; +using namespace DGtal; +using namespace functors; +using namespace Z2i; + +std::vector supportedBijectiveRotation = { + "OTC", "CBDR", "CDLR", "QSH" , "RBC" +}; + + +template +bool testBijectiveRotations(TBijectiveRotation& bijectiveRot) { + typedef ImageSelector::Type GrayImage; + typedef ImageSelector::Type ColorImage; + + typedef ForwardRigidTransformation2D < Space > ForwardTrans; + typedef DomainRigidTransformation2D < Domain, ForwardTrans > MyDomainTransformer; + typedef MyDomainTransformer::Bounds Bounds; + + Point A(0,0); + Point B(200,200); + HyperRectDomain my_domain(A,B); + + GrayImage imgGray( my_domain ); + ColorImage imgColor( my_domain ); + + std::string structName = bijectiveRot.tostring(); + + if (std::find(supportedBijectiveRotation.begin(), supportedBijectiveRotation.end(), structName) != supportedBijectiveRotation.end()) { + trace.beginBlock ( "Bijective Rotation : "+ structName); + trace.info() << bijectiveRot.tostring() << std::endl; + + auto rotatedImgGray = bijectiveRot.rotateImage(imgGray); + auto rotatedImgColor = bijectiveRot.rotateImage(imgColor); + trace.endBlock (); + } else { + return false; + } + + return true; +} + +/// check that RDSL with a Linf error results in the same domain as RDSL with a mix of 1*Linf and 0*Lcontinuity +bool testCDLRPolicy(const Point& c, const double angle) { + typedef ImageSelector::Type GrayImage; + typedef ImageSelector::Type ColorImage; + + typedef ForwardRigidTransformation2D < Space > ForwardTrans; + typedef DomainRigidTransformation2D < Domain, ForwardTrans > MyDomainTransformer; + typedef MyDomainTransformer::Bounds Bounds; + + + auto linf = std::make_shared,DGtal::HyperRectDomain< DGtal::SpaceND< 2, DGtal::int32_t >>,DGtal::CDLR_naiverotation>>>(); + auto linfWithMix = std::make_shared,DGtal::HyperRectDomain< DGtal::SpaceND< 2, DGtal::int32_t >>,DGtal::CDLR_naiverotation>>>(0.0,1.0); + DGtal::CDLR > rotCDLRLinf(angle, c, linf); + DGtal::CDLR > rotCDLRLinf_withMix(angle, c, linfWithMix); + + Point A(0,0); + Point B(200,200); + HyperRectDomain my_domain(A,B); + GrayImage imgGray( my_domain ); + + bool isSameTransformedDomain = true; + for (typename Domain::ConstIterator it = imgGray.domain().begin(); it != imgGray.domain().end(); ++it ) + { + Point cdlrLinf = rotCDLRLinf(*it); + Point cdlrLinf_withMix = rotCDLRLinf_withMix(*it); + isSameTransformedDomain *= (cdlrLinf_withMix==cdlrLinf); + } + return isSameTransformedDomain; +} + +/// check that CBDR with a Linf error results in the same domain as RDSL with a mix of 1*Linf and 0*Lcontinuity +bool testCBDRPolicy(const Point& c, const double angle) { + typedef ImageSelector::Type GrayImage; + typedef ImageSelector::Type ColorImage; + + typedef ForwardRigidTransformation2D < Space > ForwardTrans; + typedef DomainRigidTransformation2D < Domain, ForwardTrans > MyDomainTransformer; + typedef MyDomainTransformer::Bounds Bounds; + int kmax = 10; int n = 2; + + + auto linf = std::make_shared,DGtal::HyperRectDomain< DGtal::SpaceND< 2, DGtal::int32_t >>,DGtal::CBDR_naiverotation>>>(); + auto linfWithMix = std::make_shared,DGtal::HyperRectDomain< DGtal::SpaceND< 2, DGtal::int32_t >>,DGtal::CBDR_naiverotation>>>(1.0,0.0); + DGtal::CBDR,DGtal::Z2i::RealPoint> rotCBDRLinf(angle, c,n,kmax, linf); + DGtal::CBDR,DGtal::Z2i::RealPoint> rotCBDRLinf_withMix(angle, c,n,kmax, linfWithMix); + + Point A(0,0); + Point B(200,200); + HyperRectDomain my_domain(A,B); + GrayImage imgGray( my_domain ); + + bool isSameTransformedDomain = true; + for (typename Domain::ConstIterator it = imgGray.domain().begin(); it != imgGray.domain().end(); ++it ) + { + Point cbdrLinf = rotCBDRLinf(*it); + Point cbdrLinf_withMix = rotCBDRLinf_withMix(*it); + isSameTransformedDomain *= (cbdrLinf_withMix==cbdrLinf); + } + return isSameTransformedDomain; +} + + +int main() { + typedef ImageSelector::Type Image; + //! [def] + typedef ForwardRigidTransformation2D < Space > ForwardTrans; + typedef DomainRigidTransformation2D < Domain, ForwardTrans > MyDomainTransformer; + typedef MyDomainTransformer::Bounds Bounds; + + /// init + double angle = M_PI_4; + Point c = {100,100}; + const int W=200; const int H=200; + trace.beginBlock ( "Testing bijective rotations" ); + + /// QSH + DGtal::QSH> rot_QSH(angle,c); + + /// Linf DSL + auto linf = std::make_shared,DGtal::HyperRectDomain< DGtal::SpaceND< 2, DGtal::int32_t >>,DGtal::CDLR_naiverotation>>>(); + DGtal::CDLR > rot_RDSL(angle, c, linf); + + /// Linf CBDR + auto linfCBDR = std::make_shared,DGtal::HyperRectDomain< DGtal::SpaceND< 2, DGtal::int32_t >>,DGtal::CBDR_naiverotation>>>(); + const int n = 3;const int kmax=15; + DGtal::CBDR,DGtal::Z2i::RealPoint> rot_CBDR(angle,c,n,kmax,linfCBDR); + + /// RBC + DGtal::RBC_vec,DGtal::Z2i::RealPoint> rot_rbcvec(2*max(W,H)); + rot_rbcvec.setAngle() = angle; + rot_rbcvec.center() = c; + DGtal::RBC,DGtal::Z2i::RealPoint> rot_RBC(rot_rbcvec,angle,c); + + /// OTC + int rwidth = 2; + std::vector< std::vector< int > > tableOTC = DGtal::functions::loadOTCTable>("../tables/",rwidth); + DGtal::OTC,DGtal::Z2i::RealPoint> rot_OTC( tableOTC, rwidth, c, W, H ); + + + bool res = testBijectiveRotations>>(rot_OTC) && + testBijectiveRotations>>(rot_RBC) && + testBijectiveRotations>>(rot_CBDR) && + testBijectiveRotations>>(rot_RDSL) && + testBijectiveRotations>>(rot_QSH); + + + // equal domain + res = res&& testCDLRPolicy({100,100}, M_PI_4); + res = res && testCBDRPolicy({100,100}, M_PI_4); + + trace.emphase() << ( res ? "Passed." : "Error." ) << endl; + trace.endBlock(); + + + return 0; +} \ No newline at end of file