void test_linear_ring() { trace.beginBlock("linear ring"); const Domain domain(Point(-5,-5), Point(5,5)); typedef DiscreteExteriorCalculus<1, 2, EigenLinearAlgebraBackend> Calculus; Calculus calculus; calculus.initKSpace<Domain>(domain); for (int kk=-8; kk<10; kk++) calculus.insertSCell( calculus.myKSpace.sCell(Point(-8,kk), kk%2 == 0 ? Calculus::KSpace::POS : Calculus::KSpace::NEG) ); for (int kk=-8; kk<10; kk++) calculus.insertSCell( calculus.myKSpace.sCell(Point(kk,10), kk%2 == 0 ? Calculus::KSpace::POS : Calculus::KSpace::NEG) ); for (int kk=10; kk>-8; kk--) calculus.insertSCell( calculus.myKSpace.sCell(Point(10,kk)) ); for (int kk=10; kk>-8; kk--) calculus.insertSCell( calculus.myKSpace.sCell(Point(kk,-8)) ); calculus.updateIndexes(); { trace.info() << calculus << endl; Board2D board; board << domain; board << calculus; board.saveSVG("ring_structure.svg"); } const Calculus::PrimalDerivative0 d0 = calculus.derivative<0, PRIMAL>(); display_operator_info("d0", d0); const Calculus::PrimalHodge0 h0 = calculus.hodge<0, PRIMAL>(); display_operator_info("h0", h0); const Calculus::DualDerivative0 d0p = calculus.derivative<0, DUAL>(); display_operator_info("d0p", d0p); const Calculus::PrimalHodge1 h1 = calculus.hodge<1, PRIMAL>(); display_operator_info("h1", h1); const Calculus::PrimalIdentity0 laplace = calculus.laplace<PRIMAL>(); display_operator_info("laplace", laplace); const int laplace_size = calculus.kFormLength(0, PRIMAL); const Eigen::MatrixXd laplace_dense(laplace.myContainer); for (int ii=0; ii<laplace_size; ii++) FATAL_ERROR( laplace_dense(ii,ii) == 2 ); FATAL_ERROR( laplace_dense.array().rowwise().sum().abs().sum() == 0 ); FATAL_ERROR( laplace_dense.transpose() == laplace_dense ); trace.endBlock(); }
void set_initial_phase_null(Calculus& calculus, Eigen::VectorXcd& initial_wave) { typedef typename Calculus::Cell Cell; typedef typename Calculus::Index Index; typedef typename Calculus::Point Point; //! [time_phase_null] for (int xx=9; xx<13; xx++) for (int yy=13; yy<17; yy++) { const Point point(xx,yy); const Cell cell = calculus.myKSpace.uSpel(point); const Index index = calculus.getCellIndex(cell); initial_wave(index) = 1; } //! [time_phase_null] }
void set_initial_phase_dir_yy(Calculus& calculus, Eigen::VectorXcd& initial_wave) { typedef typename Calculus::Cell Cell; typedef typename Calculus::Index Index; typedef typename Calculus::Point Point; //! [time_phase_yy] for (int xx=9; xx<13; xx++) for (int yy=13; yy<17; yy++) { const Point point(xx,yy); const Cell cell = calculus.myKSpace.uSpel(point); const Index index = calculus.getCellIndex(cell); initial_wave(index) = std::exp(std::complex<double>(0,2*M_PI*(yy-14.5)/8)); } //! [time_phase_yy] }
void test_linear_structure() { trace.beginBlock("creating dec problem with neumann border condition"); //! [neumann-creation] typedef DiscreteExteriorCalculus<1, 2, EigenLinearAlgebraBackend> Calculus; typedef std::list<Calculus::SCell> SCells; SCells cells; // fill cells container { const Calculus::KSpace kspace; for (int kk=20; kk>0; kk--) cells.push_back( kspace.sCell(Point(0,kk), kk%2 != 0 ? Calculus::KSpace::NEG : Calculus::KSpace::POS) ); for (int kk=0; kk<10; kk++) cells.push_back( kspace.sCell(Point(kk,0)) ); for (int kk=0; kk<10; kk++) cells.push_back( kspace.sCell(Point(10,kk)) ); cells.push_back( kspace.sCell(Point(10,10)) ); cells.push_back( kspace.sCell(Point(9,10), Calculus::KSpace::NEG) ); for (int kk=10; kk<20; kk++) cells.push_back( kspace.sCell(Point(8,kk)) ); for (int kk=8; kk<12; kk++) cells.push_back( kspace.sCell(Point(kk,20)) ); for (int kk=20; kk>0; kk--) cells.push_back( kspace.sCell(Point(12,kk), kk%2 != 0 ? Calculus::KSpace::NEG : Calculus::KSpace::POS) ); cells.push_back( kspace.sCell(Point(12,0)) ); } // fill calculus const Domain domain(Point(-1,-1), Point(10,10)); // create DEC structure Calculus calculus; calculus.initKSpace<Domain>(domain); for (SCells::const_iterator ci=cells.begin(), ce=cells.end(); ci!=ce; ci++) calculus.insertSCell( *ci ); calculus.updateIndexes(); //! [neumann-creation] trace.info() << calculus << endl; //! [input-dirac] Calculus::PrimalForm0 dirac = Calculus::PrimalForm0::dirac(calculus, calculus.myKSpace.uCell(Point(10,4))); //! [input-dirac] { Board2D board; board << domain; board << calculus; board << dirac; board.saveSVG("linear_structure_neumann_dirac.svg"); } trace.endBlock(); { trace.beginBlock("solving problem with neumann border condition using sparse qr solver"); //! [neumann-laplace-definition] const Calculus::PrimalIdentity0 laplace = calculus.laplace<PRIMAL>(); //! [neumann-laplace-definition] trace.info() << "laplace = " << laplace << endl; const Calculus::PrimalIdentity0 reorder = calculus.reorder<0, PRIMAL>(cells.begin(), cells.end()); const Calculus::PrimalIdentity0 laplace_ordered = reorder * laplace * reorder.transpose(); trace.info() << Eigen::MatrixXd(laplace_ordered.myContainer) << endl; //! [neumann-solve] typedef EigenLinearAlgebraBackend::SolverSparseQR LinearAlgebraSolver; typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, PRIMAL, 0, PRIMAL> Solver; Solver solver; solver.compute(laplace); Calculus::PrimalForm0 solved_solution = solver.solve(dirac); //! [neumann-solve] solved_solution.myContainer.array() -= solved_solution.myContainer(38); solved_solution.myContainer.array() /= solved_solution.myContainer.maxCoeff(); const Calculus::PrimalForm0 solved_solution_ordered = reorder * solved_solution; Calculus::PrimalForm0 analytic_solution(calculus); { const Calculus::Index dirac_position = 17; const Calculus::Index length = analytic_solution.length(); for (Calculus::Index kk=0; kk<length; kk++) { Calculus::Scalar alpha = 1. * (kk)/dirac_position * (kk+1.)/(dirac_position+1.); if (kk>dirac_position) { alpha = 1. * (length-kk)/dirac_position * (length-kk-1.)/(dirac_position+1); alpha -= 1. * (length-dirac_position)/dirac_position * (length-dirac_position-1.)/(dirac_position+1); alpha += 1; } analytic_solution.myContainer(kk) = alpha; } } trace.info() << solver.isValid() << " " << solver.myLinearAlgebraSolver.info() << endl; { std::ofstream handle("linear_structure_neumann.dat"); for (Calculus::Index kk=0; kk<analytic_solution.length(); kk++) handle << solved_solution_ordered.myContainer(kk) << " " << analytic_solution.myContainer(kk) << endl; } FATAL_ERROR( (solved_solution_ordered-analytic_solution).myContainer.array().abs().maxCoeff() < 1e-5 ); { Board2D board; board << domain; board << calculus; board << solved_solution; board.saveSVG("linear_structure_neumann_solution.svg"); } { Calculus::PrimalForm1 solved_solution_gradient = calculus.derivative<0, PRIMAL>() * solved_solution; Board2D board; board << domain; board << calculus; board << solved_solution_gradient; board << CustomStyle("VectorField", new VectorFieldStyle2D(1)); board << calculus.sharp(solved_solution_gradient); board.saveSVG("linear_structure_neumann_solution_gradient.svg"); } trace.endBlock(); } trace.beginBlock("creating dec problem with dirichlet border condition"); //! [dirichlet-creation] calculus.insertSCell( calculus.myKSpace.sCell(Point(13,0)) ); calculus.insertSCell( calculus.myKSpace.sCell(Point(1,20), Calculus::KSpace::NEG) ); calculus.updateIndexes(); //! [dirichlet-creation] { Board2D board; board << domain; board << calculus; board << dirac; board.saveSVG("linear_structure_dirichlet_dirac.svg"); } trace.endBlock(); { trace.beginBlock("solving problem with dirichlet border condition using sparse qr solver"); //! [dirichlet-laplace-definition] const Calculus::PrimalIdentity0 laplace = calculus.laplace<PRIMAL>(); //! [dirichlet-laplace-definition] trace.info() << "laplace = " << laplace << endl; const Calculus::PrimalIdentity0 reorder = calculus.reorder<0, PRIMAL>(cells.begin(), cells.end()); const Calculus::PrimalIdentity0 laplace_ordered = reorder * laplace * reorder.transpose(); trace.info() << Eigen::MatrixXd(laplace_ordered.myContainer) << endl; //! [dirichlet-solve] typedef EigenLinearAlgebraBackend::SolverSparseQR LinearAlgebraSolver; typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, PRIMAL, 0, PRIMAL> Solver; Solver solver; solver.compute(laplace); Calculus::PrimalForm0 solved_solution = solver.solve(dirac); //! [dirichlet-solve] solved_solution.myContainer.array() /= solved_solution.myContainer.maxCoeff(); const Calculus::PrimalForm0 solved_solution_ordered = reorder * solved_solution; Calculus::PrimalForm0 analytic_solution(calculus); { const Calculus::Index dirac_position = 17; const Calculus::Index length = analytic_solution.length(); for (Calculus::Index kk=0; kk<length; kk++) { Calculus::Scalar alpha = (kk+1.)/(dirac_position+1.); if (kk>dirac_position) { alpha = 1. - (kk-dirac_position)/(1.*length-dirac_position); } analytic_solution.myContainer(kk) = alpha; } } trace.info() << solver.isValid() << " " << solver.myLinearAlgebraSolver.info() << endl; { std::ofstream handle("linear_structure_dirichlet.dat"); for (Calculus::Index kk=0; kk<analytic_solution.length(); kk++) handle << solved_solution_ordered.myContainer(kk) << " " << analytic_solution.myContainer(kk) << endl; } FATAL_ERROR( (solved_solution_ordered-analytic_solution).myContainer.array().abs().maxCoeff() < 1e-5 ); { Board2D board; board << domain; board << calculus; board << solved_solution; board.saveSVG("linear_structure_dirichlet_solution.svg"); } { Calculus::PrimalForm1 solved_solution_gradient = calculus.derivative<0, PRIMAL>() * solved_solution; Board2D board; board << domain; board << calculus; board << solved_solution_gradient; board << calculus.sharp(solved_solution_gradient); board.saveSVG("linear_structure_dirichlet_solution_gradient.svg"); } trace.endBlock(); } }
void test_manual_operators_3d() { trace.beginBlock("testing 3d operators"); const Z3i::Domain domain(Z3i::Point(0,0,0), Z3i::Point(1,1,1)); typedef DiscreteExteriorCalculus<3, 3, EigenLinearAlgebraBackend> Calculus; Calculus calculus; calculus.initKSpace<Z3i::Domain>(domain); { // filling primal calculus // 0-cells calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(0,0,0)) ); calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(2,0,0)) ); // 1-cells calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(0,1,0), Calculus::KSpace::POS) ); calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(0,0,1), Calculus::KSpace::POS) ); calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(1,0,0), Calculus::KSpace::POS) ); calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(2,1,0), Calculus::KSpace::NEG) ); calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(2,0,1), Calculus::KSpace::NEG) ); // 2-cells calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(0,1,1), Calculus::KSpace::NEG) ); calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(1,0,1), Calculus::KSpace::POS) ); //FIXME strange cell orientation calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(2,1,1), Calculus::KSpace::POS) ); calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(1,1,0), Calculus::KSpace::NEG) ); // 3-cells calculus.insertSCell( calculus.myKSpace.sCell(Z3i::Point(1,1,1)) ); calculus.updateIndexes(); trace.info() << calculus << endl; } trace.beginBlock("base operators"); { const Calculus::PrimalDerivative0 d0 = calculus.derivative<0, PRIMAL>(); const Calculus::DualDerivative2 d2p = calculus.derivative<2, DUAL>(); display_operator_info("d0", d0); display_operator_info("d2p", d2p); Eigen::MatrixXd d0_th(5, 2); d0_th << -1, 0, -1, 0, -1, 1, 0, 1, 0, 1; FATAL_ERROR( Eigen::MatrixXd(d0.myContainer) == d0_th ); FATAL_ERROR( Eigen::MatrixXd(d2p.myContainer) == d0_th.transpose() ); } { const Calculus::PrimalDerivative1 d1 = calculus.derivative<1, PRIMAL>(); const Calculus::DualDerivative1 d1p = calculus.derivative<1, DUAL>(); display_operator_info("d1", d1); display_operator_info("d1p", d1p); Eigen::MatrixXd d1_th(4, 5); d1_th << 0, -1, 1, 0, -1, 1, 0, -1, 1, 0, 0, 0, 0, -1, 1, -1, 1, 0, 0, 0; FATAL_ERROR( Eigen::MatrixXd(d1.myContainer) == d1_th ); FATAL_ERROR( Eigen::MatrixXd(d1p.myContainer) == d1_th.transpose() ); } { const Calculus::PrimalDerivative2 d2 = calculus.derivative<2, PRIMAL>(); const Calculus::DualDerivative0 d0p = calculus.derivative<0, DUAL>(); display_operator_info("d2", d2); display_operator_info("d0p", d0p); Eigen::MatrixXd d2_th(1, 4); d2_th << -1, -1, -1, -1; FATAL_ERROR( Eigen::MatrixXd(d2.myContainer) == d2_th ); FATAL_ERROR( Eigen::MatrixXd(d0p.myContainer) == d2_th.transpose() ); } trace.endBlock(); /* QApplication app(0, NULL); typedef Viewer3D<Z3i::Space, Z3i::KSpace> Viewer; Viewer* viewer = new Viewer(calculus.myKSpace); viewer->show(); viewer->setWindowTitle("structure"); (*viewer) << CustomColors3D(DGtal::Color(255,0,0), DGtal::Color(0,0,0)); (*viewer) << domain; Display3DFactory<Z3i::Space, Z3i::KSpace>::draw(*viewer, calculus); (*viewer) << Viewer::updateDisplay; app.exec(); */ trace.endBlock(); }
void propa_2d() { trace.beginBlock("2d propagation"); typedef DiscreteExteriorCalculus<2, 2, EigenLinearAlgebraBackend> Calculus; typedef DiscreteExteriorCalculusFactory<EigenLinearAlgebraBackend> CalculusFactory; { trace.beginBlock("solving time dependent equation"); const Calculus::Scalar cc = 8; // px/s trace.info() << "cc = " << cc << endl; const Z2i::Domain domain(Z2i::Point(0,0), Z2i::Point(29,29)); const Calculus calculus = CalculusFactory::createFromDigitalSet(generateDiskSet(domain), false); //! [time_laplace] const Calculus::DualIdentity0 laplace = calculus.laplace<DUAL>() + 1e-8 * calculus.identity<0, DUAL>(); //! [time_laplace] trace.info() << "laplace = " << laplace << endl; trace.beginBlock("finding eigen pairs"); //! [time_eigen] typedef Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> EigenSolverMatrix; const EigenSolverMatrix eigen_solver(laplace.myContainer); const Eigen::VectorXd eigen_values = eigen_solver.eigenvalues(); const Eigen::MatrixXd eigen_vectors = eigen_solver.eigenvectors(); //! [time_eigen] trace.info() << "eigen_values_range = " << eigen_values.minCoeff() << "/" << eigen_values.maxCoeff() << endl; trace.endBlock(); //! [time_omega] const Eigen::VectorXd angular_frequencies = cc * eigen_values.array().sqrt(); //! [time_omega] Eigen::VectorXcd initial_wave = Eigen::VectorXcd::Zero(calculus.kFormLength(0, DUAL)); //set_initial_phase_null(calculus, initial_wave); set_initial_phase_dir_yy(calculus, initial_wave); { Board2D board; board << domain; board << CustomStyle("KForm", new KFormStyle2D(-1, 1)); board << Calculus::DualForm0(calculus, initial_wave.real()); board.saveSVG("propagation_time_wave_initial_coarse.svg"); } //! [time_init_proj] Eigen::VectorXcd initial_projections = eigen_vectors.transpose() * initial_wave; //! [time_init_proj] // low pass //! [time_low_pass] const Calculus::Scalar lambda_cutoff = 4.5; const Calculus::Scalar angular_frequency_cutoff = 2*M_PI * cc / lambda_cutoff; int cutted = 0; for (int kk=0; kk<initial_projections.rows(); kk++) { const Calculus::Scalar angular_frequency = angular_frequencies(kk); if (angular_frequency < angular_frequency_cutoff) continue; initial_projections(kk) = 0; cutted ++; } //! [time_low_pass] trace.info() << "cutted = " << cutted << "/" << initial_projections.rows() << endl; { const Eigen::VectorXcd wave = eigen_vectors * initial_projections; Board2D board; board << domain; board << CustomStyle("KForm", new KFormStyle2D(-1, 1)); board << Calculus::DualForm0(calculus, wave.real()); board.saveSVG("propagation_time_wave_initial_smoothed.svg"); } const int kk_max = 60; trace.progressBar(0,kk_max); for (int kk=0; kk<kk_max; kk++) { //! [time_solve_time] const Calculus::Scalar time = kk/20.; const Eigen::VectorXcd current_projections = (angular_frequencies * std::complex<double>(0,time)).array().exp() * initial_projections.array(); const Eigen::VectorXcd current_wave = eigen_vectors * current_projections; //! [time_solve_time] std::stringstream ss; ss << "propagation_time_wave_solution_" << std::setfill('0') << std::setw(3) << kk << ".svg"; Board2D board; board << domain; board << CustomStyle("KForm", new KFormStyle2D(-1, 1)); board << Calculus::DualForm0(calculus, current_wave.real()); board.saveSVG(ss.str().c_str()); trace.progressBar(kk+1,kk_max); } trace.info() << endl; trace.endBlock(); } { trace.beginBlock("forced oscillations"); const Z2i::Domain domain(Z2i::Point(0,0), Z2i::Point(50,50)); const Calculus calculus = CalculusFactory::createFromDigitalSet(generateDiskSet(domain), false); const Calculus::DualIdentity0 laplace = calculus.laplace<DUAL>(); trace.info() << "laplace = " << laplace << endl; trace.beginBlock("finding eigen pairs"); typedef Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> EigenSolverMatrix; const EigenSolverMatrix eigen_solver(laplace.myContainer); const Eigen::VectorXd laplace_eigen_values = eigen_solver.eigenvalues(); const Eigen::MatrixXd laplace_eigen_vectors = eigen_solver.eigenvectors(); trace.info() << "eigen_values_range = " << laplace_eigen_values.minCoeff() << "/" << laplace_eigen_values.maxCoeff() << endl; trace.endBlock(); Calculus::DualForm0 concentration(calculus); { const Z2i::Point point(25,25); const Calculus::Cell cell = calculus.myKSpace.uSpel(point); const Calculus::Index index = calculus.getCellIndex(cell); concentration.myContainer(index) = 1; } { Board2D board; board << domain; board << concentration; board.saveSVG("propagation_forced_concentration.svg"); } for (int ll=0; ll<6; ll++) { //! [forced_lambda] const Calculus::Scalar lambda = 4*20.75/(1+2*ll); //! [forced_lambda] trace.info() << "lambda = " << lambda << endl; //! [forced_dalembert_eigen] const Eigen::VectorXd dalembert_eigen_values = (laplace_eigen_values.array() - (2*M_PI/lambda)*(2*M_PI/lambda)).array().inverse(); const Eigen::MatrixXd concentration_to_wave = laplace_eigen_vectors * dalembert_eigen_values.asDiagonal() * laplace_eigen_vectors.transpose(); //! [forced_dalembert_eigen] //! [forced_wave] Calculus::DualForm0 wave(calculus, concentration_to_wave * concentration.myContainer); //! [forced_wave] wave.myContainer /= wave.myContainer(calculus.getCellIndex(calculus.myKSpace.uSpel(Z2i::Point(25,25)))); { trace.info() << "saving samples" << endl; const Calculus::Properties properties = calculus.getProperties(); { std::stringstream ss; ss << "propagation_forced_samples_hv_" << ll << ".dat"; std::ofstream handle(ss.str().c_str()); for (int kk=0; kk<=50; kk++) { const Z2i::Point point_horizontal(kk,25); const Z2i::Point point_vertical(25,kk); const Calculus::Scalar xx = 2 * (kk/50. - .5); handle << xx << " "; handle << sample_dual_0_form<Calculus>(properties, wave, point_horizontal) << " "; handle << sample_dual_0_form<Calculus>(properties, wave, point_vertical) << endl; } } { std::stringstream ss; ss << "propagation_forced_samples_diag_" << ll << ".dat"; std::ofstream handle(ss.str().c_str()); for (int kk=0; kk<=50; kk++) { const Z2i::Point point_diag_pos(kk,kk); const Z2i::Point point_diag_neg(kk,50-kk); const Calculus::Scalar xx = 2 * sqrt(2) * (kk/50. - .5); handle << xx << " "; handle << sample_dual_0_form<Calculus>(properties, wave, point_diag_pos) << " "; handle << sample_dual_0_form<Calculus>(properties, wave, point_diag_neg) << endl; } } } { std::stringstream ss; ss << "propagation_forced_wave_" << ll << ".svg"; Board2D board; board << domain; board << CustomStyle("KForm", new KFormStyle2D(-.5, 1)); board << wave; board.saveSVG(ss.str().c_str()); } } trace.endBlock(); } trace.endBlock(); }
int main(int argc, char* argv[]) { using DGtal::trace; using std::endl; using DGtal::PRIMAL; using DGtal::DUAL; const Options options = parse_options(argc, argv); const KSpace kspace; const Calculus calculus; const FlatVector original_face_normals; if (!ends_with(options.image_filename, ".csv")) { ASSERT( !options.image_filename.empty() ); typedef DGtal::Z3i::Domain Domain; typedef DGtal::ImageSelector<Domain, unsigned char>::Type ImageUChar; trace.info() << "image_filename=" << options.image_filename << endl; ImageUChar image_uchar = DGtal::GenericReader<ImageUChar>::import(options.image_filename); const Domain domain = image_uchar.domain(); trace.info() << "domain=" << domain << endl; const Point center = (domain.upperBound()+domain.lowerBound())/2; trace.info() << "center=" << center << endl; const ImageShape<ImageUChar> shape(&image_uchar, center); const_cast<KSpace&>(kspace).init(domain.lowerBound()-center-Point::diagonal(1), domain.upperBound()-center+Point::diagonal(1), true); std::tie(const_cast<Calculus&>(calculus), const_cast<FlatVector&>(original_face_normals)) = initCalculusAndNormalsWithNoise(kspace, shape, options.normal_radius, options.noise_level); } if (ends_with(options.image_filename, ".csv")) { ASSERT( !options.image_filename.empty() ); trace.info() << "csv_filename=" << options.image_filename << endl; std::tie(const_cast<Calculus&>(calculus), const_cast<FlatVector&>(original_face_normals)) = initCalculusAndNormalsFromSurfelNormalsCSV(options.image_filename); } const FlatVector original_vertex_normals = vertexNormals(calculus, original_face_normals); const FlatVector original_positions; const FlatVector regularized_positions; const FlatVector original_centers; const FlatVector regularized_centers; std::tie(const_cast<FlatVector&>(original_positions), const_cast<FlatVector&>(regularized_positions), const_cast<FlatVector&>(original_centers), const_cast<FlatVector&>(regularized_centers)) = approximateSurface(calculus, original_face_normals, ApproxParams({options.regularization_position, options.regularization_center, options.align, options.fairness, options.barycenter})); ASSERT( original_positions.size() == 3*calculus.kFormLength(0, PRIMAL) ); ASSERT( regularized_positions.size() == 3*calculus.kFormLength(0, PRIMAL) ); ASSERT( original_centers.size() == 3*calculus.kFormLength(2, PRIMAL) ); ASSERT( regularized_centers.size() == 3*calculus.kFormLength(2, PRIMAL) ); { trace.beginBlock( "computing energies" ); { double position_energy = 0; double align_energy = 0; std::tie( position_energy, align_energy ) = approximateSurfaceEnergies( calculus, original_face_normals, original_positions ); align_energy *= options.align; position_energy *= options.regularization_position; trace.info() << "original_energies=" << position_energy << " " << align_energy << " " << position_energy + align_energy << endl; } { double position_energy = 0; double align_energy = 0; std::tie(position_energy, align_energy) = approximateSurfaceEnergies(calculus, original_face_normals, regularized_positions); align_energy *= options.align; position_energy *= options.regularization_position; trace.info() << "regularized_energies=" << position_energy << " " << align_energy << " " << position_energy+align_energy << endl; } trace.endBlock(); } { ASSERT( !options.regularized_obj_filename.empty() ); trace.info() << "regularized_obj_filename=" << options.regularized_obj_filename << endl; exportOBJ(calculus, regularized_positions, options.regularized_obj_filename); } if (!options.cubical_obj_filename.empty()) { ASSERT( !options.cubical_obj_filename.empty() ); trace.info() << "cubical_obj_filename=" << options.cubical_obj_filename << endl; exportOBJ(calculus, original_positions, options.cubical_obj_filename); } return 0; }
int main( int argc, char* argv[] ) { using namespace Z2i; typedef ImageSelector<Domain, unsigned char>::Type Image; // parse command line ---------------------------------------------- namespace po = boost::program_options; po::options_description general_opt("Allowed options are: "); general_opt.add_options() ("help,h", "display this message") ("input,i", po::value<string>(), "the input image filename." ) ("output,o", po::value<string>()->default_value( "AT" ), "the output image basename." ) ("lambda,l", po::value<double>(), "the parameter lambda." ) ("lambda-1,1", po::value<double>()->default_value( 0.3125 ), "the initial parameter lambda (l1)." ) // 0.3125 ("lambda-2,2", po::value<double>()->default_value( 0.00005 ), "the final parameter lambda (l2)." ) ("lambda-ratio,q", po::value<double>()->default_value( sqrt(2) ), "the division ratio for lambda from l1 to l2." ) ("alpha,a", po::value<double>()->default_value( 1.0 ), "the parameter alpha." ) ("epsilon,e", po::value<double>()->default_value( 4.0/64.0 ), "the parameter epsilon." ) //("gridstep,g", po::value<double>()->default_value( 1.0 ), "the parameter h, i.e. the gridstep." ) ("nbiter,n", po::value<int>()->default_value( 10 ), "the maximum number of iterations." ) ("sigma,s", po::value<double>()->default_value( 2.0 ), "the parameter of the first convolution." ) ("rho,r", po::value<double>()->default_value( 3.0 ), "the parameter of the second convolution." ) ("image-size,t", po::value<double>()->default_value( 64.0 ), "the size of the image." ) ; bool parseOK=true; po::variables_map vm; try { po::store(po::parse_command_line(argc, argv, general_opt), vm); } catch ( const exception& ex ) { parseOK = false; cerr << "Error checking program options: "<< ex.what()<< endl; } po::notify(vm); if ( ! parseOK || vm.count("help") || !vm.count("input") ) { cerr << "Usage: " << argv[0] << " -i toto.pgm\n" << "Computes the Ambrosio-Tortorelli reconstruction/segmentation of an input image." << endl << endl << " / " << endl << " | a.(u-g)^2 + v^2 |grad u|^2 + le.|grad v|^2 + (l/4e).(1-v)^2 " << endl << " / " << endl << endl << general_opt << "\n"; return 1; } string f1 = vm[ "input" ].as<string>(); string f2 = vm[ "output" ].as<string>(); double l1 = vm[ "lambda-1" ].as<double>(); double l2 = vm[ "lambda-2" ].as<double>(); double lr = vm[ "lambda-ratio" ].as<double>(); if ( vm.count( "lambda" ) ) l1 = l2 = vm[ "lambda" ].as<double>(); if ( l2 > l1 ) l2 = l1; if ( lr <= 1.0 ) lr = sqrt(2); double a = vm[ "alpha" ].as<double>(); double e = vm[ "epsilon" ].as<double>(); double t = vm[ "image-size" ].as<double>(); //double h = vm[ "gridstep" ].as<double>(); double h = 1.0 / t; e = 4.0*h; int n = vm[ "nbiter" ].as<int>(); int s = vm[ "sigma" ].as<double>(); int r = vm[ "rho" ].as<double>(); trace.beginBlock("Reading image"); Image image = GenericReader<Image>::import( f1 ); trace.endBlock(); // MODIF : ouverture du fichier pour les resultats *********************************************************************** const string file = f2 + ".txt"; ofstream f(file.c_str()); f << "# l \t" << " a \t" << " e \t" << "a(u-g)^2 \t" << "v^2|grad u|^2 \t" << " le|grad v|^2 \t" << " l(1-v)^2/4e \t" << " l.per \t" << "AT tot"<< endl; // *********************************************************************************************************************** trace.beginBlock("Creating calculus"); typedef DiscreteExteriorCalculus<2,2, EigenLinearAlgebraBackend> Calculus; Domain domain = image.domain(); Point p0 = domain.lowerBound(); p0 *= 2; Point p1 = domain.upperBound(); p1 *= 2; Domain kdomain( p0, p1 ); Image dbl_image( kdomain ); Calculus calculus; const KSpace& K = calculus.myKSpace; // Les pixels sont des 0-cellules du primal. for ( Domain::ConstIterator it = kdomain.begin(), itE = kdomain.end(); it != itE; ++it ) calculus.insertSCell( K.sCell( *it ) ); // ajoute toutes les cellules de Khalimsky. calculus.updateIndexes(); trace.info() << calculus << endl; Calculus::PrimalForm0 g( calculus ); for ( Calculus::Index index = 0; index < g.myContainer.rows(); index++) { const Calculus::SCell& cell = g.getSCell( index ); g.myContainer( index ) = ((double) image( K.sCoords( cell ) )) / 255.0; } // { // Board2D board; // board << calculus; // board << CustomStyle( "KForm", new KFormStyle2D( 0.0, 1.0 ) ) // << g; // string str_calculus = f2 + "-calculus.eps"; // board.saveEPS( str_calculus.c_str() ); // } trace.endBlock(); trace.beginBlock("building AT functionnals"); trace.info() << "primal_D0" << endl; const Calculus::PrimalDerivative0 primal_D0 = calculus.derivative<0,PRIMAL>(); trace.info() << "primal_h0" << endl; const Calculus::PrimalHodge0 primal_h0 = calculus.hodge<0,PRIMAL>(); trace.info() << "primal_h1" << endl; const Calculus::PrimalHodge1 primal_h1 = calculus.hodge<1,PRIMAL>(); trace.info() << "dual_D1" << endl; const Calculus::DualDerivative1 dual_D1 = calculus.derivative<1,DUAL>(); trace.info() << "dual_h2" << endl; const Calculus::DualHodge2 dual_h2 = calculus.hodge<2,DUAL>(); trace.info() << "primal_D1" << endl; const Calculus::PrimalDerivative1 primal_D1 = calculus.derivative<1,PRIMAL>(); trace.info() << "primal_h2" << endl; const Calculus::PrimalHodge2 primal_h2 = calculus.hodge<2,PRIMAL>(); trace.info() << "dual_D0" << endl; const Calculus::DualDerivative0 dual_D0 = calculus.derivative<0,DUAL>(); trace.info() << "dual_h1" << endl; const Calculus::DualHodge1 dual_h1 = calculus.hodge<1,DUAL>(); trace.info() << "ag" << endl; const Calculus::PrimalForm0 ag = a * g; trace.info() << "u" << endl; Calculus::PrimalForm0 u = ag; // trace.info() << "A^t*diag(v)^2*A = " << Av2A << endl; trace.info() << "v" << endl; Calculus::PrimalForm1 v( calculus ); for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++) v.myContainer( index ) = 1.0; trace.endBlock(); // SparseLU is so much faster than SparseQR // SimplicialLLT is much faster than SparsLU // typedef EigenLinearAlgebraBackend::SolverSparseQR LinearAlgebraSolver; // typedef EigenLinearAlgebraBackend::SolverSparseLU LinearAlgebraSolver; typedef EigenLinearAlgebraBackend::SolverSimplicialLLT LinearAlgebraSolver; typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, PRIMAL, 0, PRIMAL> SolverU; SolverU solver_u; typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 1, PRIMAL, 1, PRIMAL> SolverV; SolverV solver_v; SolverV solver_tBB; const Calculus::PrimalIdentity1 tBB = -1.0 * ( primal_D0 * dual_h2 * dual_D1 * primal_h1 + dual_h1 * dual_D0 * primal_h2 * primal_D1 ); solver_tBB.compute( tBB ); while ( l1 >= l2 ) { trace.info() << "************ lambda = " << l1 << " **************" << endl; double l = l1; trace.info() << "B'B'" << endl; const Calculus::PrimalIdentity1 lBB = l * tBB; // const Calculus::PrimalIdentity1 lBB = - l * ( primal_D0 * dual_h2 * dual_D1 * primal_h1 + dual_h1 * dual_D0 * primal_h2 * primal_D1 ); Calculus::PrimalForm1 l_4( calculus ); for ( Calculus::Index index = 0; index < l_4.myContainer.rows(); index++) l_4.myContainer( index ) = l/4.0; //Calculus::PrimalIdentity1 BB = eps * lBB + (l/(4*eps)) * calculus.identity<1, PRIMAL>(); //trace.info() << "le*B'^t*B' + l/(4e)Id" << BB << endl; Calculus::PrimalIdentity1 BB = 0.0 * lBB; double coef_eps = 2.0; double eps = coef_eps*e; for( int k = 0 ; k < 5 ; ++k ) { if (eps/coef_eps < h*h) break; else { eps /= coef_eps; BB = (h * eps) * lBB + (h*l/(4.0*eps)) * calculus.identity<1, PRIMAL>(); int i = 0; for ( ; i < n; ++i ) { trace.info() << "------ Iteration " << k << ":" << i << "/" << n << " ------" << endl; trace.beginBlock("Solving for u"); trace.info() << "Building matrix Av2A" << endl; Calculus::PrimalIdentity1 Mv2 = calculus.identity<1, PRIMAL>(); for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++) Mv2.myContainer.coeffRef( index, index ) = v.myContainer[ index ] * v.myContainer[ index ]; const Calculus::PrimalIdentity0 Av2A = ( - 1.0 * h ) * dual_h2 * dual_D1 * primal_h1 * Mv2 * primal_D0 + ( a * h*h ) * calculus.identity<0, PRIMAL>(); trace.info() << "Prefactoring matrix Av2A" << endl; solver_u.compute( Av2A ); trace.info() << "Solving Av2A u = ag" << endl; u = solver_u.solve( (h*h) * ag ); trace.info() << ( solver_u.isValid() ? "OK" : "ERROR" ) << " " << solver_u.myLinearAlgebraSolver.info() << endl; trace.endBlock(); trace.beginBlock("Solving for v"); trace.info() << "Building matrix BB+Mw2" << endl; const Calculus::PrimalForm1 former_v = v; const Calculus::PrimalForm1 w = primal_D0 * u; Calculus::PrimalIdentity1 Mw2 = calculus.identity<1, PRIMAL>(); for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++) Mw2.myContainer.coeffRef( index, index ) = h * w.myContainer[ index ] * w.myContainer[ index ]; trace.info() << "Prefactoring matrix BB+Mw2" << endl; solver_v.compute( BB + Mw2 ); trace.info() << "Solving (BB+Mw2)v = l_4e" << endl; v = solver_v.solve( ( h * (1.0/eps) ) * l_4 ); trace.info() << ( solver_v.isValid() ? "OK" : "ERROR" ) << " " << solver_v.myLinearAlgebraSolver.info() << endl; trace.endBlock(); double n_infty = 0.0; for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++) n_infty = max( n_infty, abs( v.myContainer( index ) - former_v.myContainer( index ) ) ); trace.info() << "Variation |v^k+1 - v^k|_oo = " << n_infty << endl; if ( n_infty < 1e-4 ) break; } //BB = eps * lBB + (l/(4*eps)) * calculus.identity<1, PRIMAL>(); } } // *** MODIF : affichage des energies *********************************************************************************** typedef Calculus::SparseMatrix SparseMatrix; typedef Eigen::Matrix<double,Dynamic,Dynamic> Matrix; trace.beginBlock("Computing energies"); // a(u-g)^2 trace.info() << "- a(u-g)^2 " << std::endl; double UmG2 = 0.0; for ( Calculus::Index index = 0; index < u.myContainer.rows(); index++) UmG2 += a * (u.myContainer( index ) - g.myContainer( index )) * (u.myContainer( index ) - g.myContainer( index )); // v^2|grad u|^2 trace.info() << "- v^2|grad u|^2" << std::endl; double V2gradU2 = 0.0; SolverU solver_Av2A; trace.info() << " - Id" << std::endl; Calculus::PrimalIdentity1 Mv2 = calculus.identity<1, PRIMAL>(); trace.info() << " - M := Diag(v^2)" << std::endl; for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++) Mv2.myContainer.coeffRef( index, index ) = v.myContainer[ index ] * v.myContainer[ index ]; trace.info() << " - * D_1 * M * D_0" << std::endl; const Calculus::PrimalIdentity0 Av2A = - 1.0 * dual_h2 * dual_D1 * primal_h1 * Mv2 * primal_D0; trace.info() << " - N := compute (* D_1 * M * D_0)" << std::endl; solver_Av2A.compute( Av2A ); // JOL: 1000 * plus rapide ! trace.info() << " - u^t N u" << std::endl; Calculus::PrimalForm0 u_prime = Av2A * u; for ( Calculus::Index index = 0; index < u.myContainer.rows(); index++) V2gradU2 += u.myContainer( index ) * u_prime.myContainer( index ); // for ( Calculus::Index index_i = 0; index_i < u.myContainer.rows(); index_i++) // for ( Calculus::Index index_j = 0; index_j < u.myContainer.rows(); index_j++) // V2gradU2 += u.myContainer( index_i ) * Av2A.myContainer.coeff( index_i,index_j ) * u.myContainer( index_j ) ; // le|grad v|^2 trace.info() << "- le|grad v|^2" << std::endl; Calculus::PrimalForm1 v_prime = tBB * v; double gradV2 = 0.0; for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++) gradV2 += l * eps * v.myContainer( index ) * v_prime.myContainer( index ); // for ( Calculus::Index index_i = 0; index_i < v.myContainer.rows(); index_i++) // for ( Calculus::Index index_j = 0; index_j < v.myContainer.rows(); index_j++) // gradV2 += l * eps * v.myContainer( index_i ) * tBB.myContainer.coeff( index_i,index_j ) * v.myContainer( index_j ); // l(1-v)^2/4e trace.info() << "- l(1-v)^2/4e" << std::endl; double Vm12 = 0.0; for ( Calculus::Index index_i = 0; index_i < v.myContainer.rows(); index_i++) Vm12 += (l/(4*eps)) * (1 - 2*v.myContainer( index_i ) + v.myContainer( index_i )*v.myContainer( index_i )); // l.per trace.info() << "- l.per" << std::endl; double Lper = h*gradV2 + h*Vm12; // double per = 0.0; // for ( Calculus::Index index_i = 0; index_i < v.myContainer.rows(); index_i++) // { // per += (1/(4*e)) * (1 - 2*v.myContainer( index_i ) + v.myContainer( index_i )*v.myContainer( index_i )); // for ( Calculus::Index index_j = 0; index_j < v.myContainer.rows(); index_j++) // per += e * v.myContainer( index_i ) * tBB.myContainer( index_i,index_j ) * v.myContainer( index_j ); // } // AT tot double ATtot = h*h*UmG2 + h*V2gradU2 + h*gradV2 + h*Vm12; //f << "l " << " a " << " e " << " a(u-g)^2 " << " v^2|grad u|^2 " << " le|grad v|^2 " << " l(1-v)^2/4e " << " l.per " << " AT tot"<< endl; f << tronc(l,8) << "\t" << a << "\t" << tronc(eps,4) << "\t" << tronc(UmG2,5) << "\t" << tronc(V2gradU2,5) << "\t" << tronc(gradV2,5) << "\t" << tronc(Vm12,5) << "\t" << tronc(Lper,5) << "\t" << tronc(ATtot,5) << endl; trace.endBlock(); // *********************************************************************************************************************** // int int_l = (int) floor(l); // int dec_l = (int) (floor((l-floor(l))*10000)); // std::ostringstream ss; // ss << (double) tronc(l,7); // string s = ss.str(); // const int pos = s.find("."); // string tronc_l = s.substr(0,pos) + "_" + s.substr(pos+1); { // Board2D board; // board << calculus; // board << CustomStyle( "KForm", new KFormStyle2D( 0.0, 1.0 ) ) << u; // ostringstream oss; // oss << f2 << "-u-" << i << ".eps"; // string str_u = oss.str(); //f2 + "-u-" + .eps"; // board.saveEPS( str_u.c_str() ); Image image2 = image; PrimalForm0ToImage( calculus, u, image2 ); ostringstream oss2; // oss2 << f2 << "-l" << int_l << "_" << dec_l << "-u.pgm"; // oss2 << f2 << "-l" << tronc_l << "-u.pgm"; oss2 << boost::format("%s-l%.7f-u.pgm") %f2 %l; string str_image_u = oss2.str(); // boost::regex re("\\."); // str_image_u = boost::regex_replace(str_image_u, re , "_"); // const int pos = str_image_u.find("."); // str_image_u = str_image_u.substr(0,pos) + "_" + str_image_u.substr(pos+1); image2 >> str_image_u.c_str(); } { // Board2D board; // board << calculus; // board << CustomStyle( "KForm", new KFormStyle2D( 0.0, 1.0 ) ) // << v; // ostringstream oss; // oss << f2 << "-v-" << i << ".eps"; // string str_v = oss.str(); // board.saveEPS( str_v.c_str() ); PrimalForm1ToImage( calculus, v, dbl_image ); ostringstream oss3; //oss3 << f2 << "-l" << tronc_l << "-v.pgm"; oss3 << boost::format("%s-l%.7f-v.pgm") %f2 %l; string str_image_v = oss3.str(); dbl_image >> str_image_v.c_str(); } l1 /= lr; } // while ( l1 >= l2 ) // typedef SelfAdjointEigenSolver<MatrixXd> EigenSolverMatrix; // const EigenSolverMatrix eigen_solver(laplace.myContainer); // const VectorXd eigen_values = eigen_solver.eigenvalues(); // const MatrixXd eigen_vectors = eigen_solver.eigenvectors(); // for (int kk=0; kk<laplace.myContainer.rows(); kk++) // { // Calculus::Scalar eigen_value = eigen_values(kk, 0); // const VectorXd eigen_vector = eigen_vectors.col(kk); // const Calculus::DualForm0 eigen_form = Calculus::DualForm0(calculus, eigen_vector); // std::stringstream ss; // ss << "chladni_eigen_" << kk << ".svg"; // const std::string filename = ss.str(); // ss << "chladni_eigen_vector_" << kk << ".svg"; // trace.info() << kk << " " << eigen_value << " " << sqrt(eigen_value) << " " << eigen_vector.minCoeff() << " " << eigen_vector.maxCoeff() << " " << standard_deviation(eigen_vector) << endl; // Board2D board; // board << domain; // board << calculus; // board << CustomStyle("KForm", new KFormStyle2D(eigen_vectors.minCoeff(),eigen_vectors.maxCoeff())); // board << eigen_form; // board.saveSVG(filename.c_str()); // } f.close(); return 0; }
int main( int argc, char* argv[] ) { using namespace Z2i; typedef ImageContainerBySTLVector<Domain, unsigned char> Image; typedef ImageContainerBySTLVector<Domain, double> ImageDouble; typedef ImageContainerBySTLVector<Domain, SimpleMatrix<double,2,2> > ImageSimpleMatrix2d; typedef ImageContainerBySTLVector<Domain, RealVector> ImageVector; typedef std::vector< unsigned char >::iterator ImageIterator; typedef std::vector< double >::iterator ImageDoubleIterator; typedef std::vector< SimpleMatrix<double,2,2> >::iterator ImageSimpleMatrix2dIterator; typedef std::vector< RealVector >::iterator ImageVectorIterator; // parse command line ---------------------------------------------- namespace po = boost::program_options; po::options_description general_opt("Allowed options are: "); general_opt.add_options() ("help,h", "display this message") ("input,i", po::value<string>(), "the input image filename." ) ("output,o", po::value<string>()->default_value( "AT" ), "the output image basename." ) ("lambda,l", po::value<double>(), "the parameter lambda." ) ("lambda-1,1", po::value<double>()->default_value( 0.3125 ), "the initial parameter lambda (l1)." ) // 0.3125 ("lambda-2,2", po::value<double>()->default_value( 0.00005 ), "the final parameter lambda (l2)." ) ("lambda-ratio,q", po::value<double>()->default_value( sqrt(2) ), "the division ratio for lambda from l1 to l2." ) ("alpha,a", po::value<double>()->default_value( 1.0 ), "the parameter alpha." ) ("epsilon,e", po::value<double>()->default_value( 1.0 ), "the initial and final parameter epsilon of AT functional at the same time." ) ("epsilon-1", po::value<double>(), "the initial parameter epsilon." ) ("epsilon-2", po::value<double>(), "the final parameter epsilon." ) ("epsilon-r", po::value<double>()->default_value( 2.0 ), "sets the ratio between two consecutive epsilon values of AT functional." ) //("gridstep,g", po::value<double>()->default_value( 1.0 ), "the parameter h, i.e. the gridstep." ) ("nbiter,n", po::value<int>()->default_value( 10 ), "the maximum number of iterations." ) //("sigma,s", po::value<double>()->default_value( 2.0 ), "the parameter of the first convolution." ) //("rho,r", po::value<double>()->default_value( 3.0 ), "the parameter of the second convolution." ) ("image-size,t", po::value<double>()->default_value( 64.0 ), "the size of the image." ) ; bool parseOK=true; po::variables_map vm; try { po::store(po::parse_command_line(argc, argv, general_opt), vm); } catch ( const exception& ex ) { parseOK = false; cerr << "Error checking program options: "<< ex.what()<< endl; } po::notify(vm); if ( ! parseOK || vm.count("help") || !vm.count("input") ) { cerr << "Usage: " << argv[0] << " -i toto.pgm\n" << "Computes the Ambrosio-Tortorelli reconstruction/segmentation of an input image." << endl << endl << " / " << endl << " | a.(u-g)^2 + v^2 |grad u|^2 + le.|grad v|^2 + (l/4e).(1-v)^2 " << endl << " / " << endl << "Discretized as (u 0-form, v 1-form, A vertex-edge bdry, B edge-face bdy)" << endl << "E(u,v) = a(u-g)^t (u-g) + u^t A^t diag(v)^2 A^t u + l e v^t (A A^t + B^t B) v + l/(4e) (1-v)^t (1-v)" << endl << endl << general_opt << "\n" << "Example: ./at-u0-v1 -i ../Images/cerclesTriangle64b02.pgm -o tmp -a 0.05 -e 1 -1 0.1 -2 0.00001 -t 64" << endl; return 1; } string f1 = vm[ "input" ].as<string>(); string f2 = vm[ "output" ].as<string>(); double l1 = vm[ "lambda-1" ].as<double>(); double l2 = vm[ "lambda-2" ].as<double>(); double lr = vm[ "lambda-ratio" ].as<double>(); if ( vm.count( "lambda" ) ) l1 = l2 = vm[ "lambda" ].as<double>(); if ( l2 > l1 ) l2 = l1; if ( lr <= 1.0 ) lr = sqrt(2); double a = vm[ "alpha" ].as<double>(); double e = vm[ "epsilon" ].as<double>(); double e1 = vm.count( "epsilon-1" ) ? vm[ "epsilon-1" ].as<double>() : e; double e2 = vm.count( "epsilon-2" ) ? vm[ "epsilon-2" ].as<double>() : e; double er = vm[ "epsilon-r" ].as<double>(); double t = vm[ "image-size" ].as<double>(); //double h = vm[ "gridstep" ].as<double>(); double h = 1.0 / t; int n = vm[ "nbiter" ].as<int>(); //double s = vm[ "sigma" ].as<double>(); //double r = vm[ "rho" ].as<double>(); trace.beginBlock("Reading image"); Image image = GenericReader<Image>::import( f1 ); Image end_image = image; trace.endBlock(); // opening file const string file = f2 + ".txt"; ofstream f(file.c_str()); f << "# l \t" << " a \t" << " e \t" << "a(u-g)^2 \t" << "v^2|grad u|^2 \t" << " le|grad v|^2 \t" << " l(1-v)^2/4e \t" << " l.per \t" << "AT tot"<< endl; trace.beginBlock("Creating calculus"); typedef DiscreteExteriorCalculus<2,2, EigenLinearAlgebraBackend> Calculus; Domain domain = image.domain(); Point p0 = domain.lowerBound(); p0 *= 2; Point p1 = domain.upperBound(); p1 *= 2; Domain kdomain( p0, p1 ); Image dbl_image( kdomain ); Calculus calculus; calculus.initKSpace( ConstAlias<Domain>( domain ) ); const KSpace& K = calculus.myKSpace; // Les pixels sont des 0-cellules du primal. for ( Domain::ConstIterator it = kdomain.begin(), itE = kdomain.end(); it != itE; ++it ) calculus.insertSCell( K.sCell( *it ) ); // ajoute toutes les cellules de Khalimsky. calculus.updateIndexes(); trace.info() << calculus << endl; Calculus::PrimalForm0 g( calculus ); double min_g = 300.0, max_g = 0.0; for ( Calculus::Index index = 0; index < g.myContainer.rows(); index++) { const Calculus::SCell& cell = g.getSCell( index ); g.myContainer( index ) = ((double) image( K.sCoords( cell ) )) / 255.0; min_g = std::min( min_g , g.myContainer( index ) ); max_g = std::max( max_g , g.myContainer( index ) ); } trace.info() << "min_g= " << min_g << " max_g= " << max_g << std::endl; trace.endBlock(); // u = g at the beginning trace.info() << "u" << endl; Calculus::PrimalForm0 u = g; // v = 1 at the beginning trace.info() << "v" << endl; Calculus::PrimalForm1 v( calculus ); for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++) v.myContainer( index ) = 1; trace.beginBlock("building AT functionnals"); trace.info() << "primal_D0" << endl; const Calculus::PrimalDerivative0 primal_D0 = calculus.derivative<0,PRIMAL>(); trace.info() << "primal_D1" << endl; const Calculus::PrimalDerivative1 primal_D1 = calculus.derivative<1,PRIMAL>(); trace.info() << "dual_D0" << endl; const Calculus::DualDerivative0 dual_D0 = calculus.derivative<0,DUAL>(); trace.info() << "dual_D1" << endl; const Calculus::DualDerivative1 dual_D1 = calculus.derivative<1,DUAL>(); trace.info() << "primal_h0" << endl; const Calculus::PrimalHodge0 primal_h0 = calculus.hodge<0,PRIMAL>(); trace.info() << "primal_h1" << endl; const Calculus::PrimalHodge1 primal_h1 = calculus.hodge<1,PRIMAL>(); trace.info() << "primal_h2" << endl; const Calculus::PrimalHodge2 primal_h2 = calculus.hodge<2,PRIMAL>(); trace.info() << "dual_h1" << endl; const Calculus::DualHodge1 dual_h1 = calculus.hodge<1,DUAL>(); trace.info() << "dual_h2" << endl; const Calculus::DualHodge2 dual_h2 = calculus.hodge<2,DUAL>(); const DGtal::Dimension dimX = 0, dimY = 1; // BEG JOL // Calculus::PrimalIdentity1 tSS = calculus.identity<1, PRIMAL>(); // for ( Calculus::Index index_i = 0; index_i < tSS.myContainer.rows(); index_i++ ) // for ( Calculus::Index index_j = 0; index_j < tSS.myContainer.cols(); index_j++ ) // { // tSS.myContainer.coeffRef( index_i, index_j ) = 0.0; // for ( Calculus::Index index_k = 0; index_k < tSS.myContainer.rows(); index_k++ ) // tSS.myContainer.coeffRef( index_i, index_j ) += sharp_x.myContainer.coeffRef( index_k, index_i ) * sharp_x.myContainer.coeffRef( index_k, index_j ) // + sharp_y.myContainer.coeffRef( index_k, index_i ) * sharp_y.myContainer.coeffRef( index_k, index_j ) ; // } // END JOL trace.endBlock(); const Calculus::PrimalIdentity0 Id0 = calculus.identity<0, PRIMAL>(); const Calculus::PrimalIdentity1 Id1 = calculus.identity<1, PRIMAL>(); const Calculus::PrimalIdentity2 Id2 = calculus.identity<2, PRIMAL>(); // Weight matrices // Calculus::DualIdentity2 G0 = ( 1.0/(h*h) ) * calculus.identity<2, DUAL>(); Calculus::PrimalIdentity0 G0 = Id0; // = ( 1.0/(h*h) ) * calculus.identity<0, PRIMAL>(); Calculus::PrimalIdentity0 invG0 = Id0; // = (h*h) * calculus.identity<0, PRIMAL>(); // Calculus::DualIdentity1 G1 = calculus.identity<1, DUAL>(); Calculus::PrimalIdentity1 G1 = Id1; // = calculus.identity<1, PRIMAL>(); Calculus::PrimalIdentity1 invG1 = Id1; // = calculus.identity<1, PRIMAL>(); // Calculus::DualIdentity0 G2 = (h*h) * calculus.identity<0, DUAL>(); Calculus::PrimalIdentity2 G2 = Id2; // = (h*h) * calculus.identity<2, PRIMAL>(); Calculus::PrimalIdentity2 invG2 = Id2; // = ( 1.0/(h*h) ) * calculus.identity<2, PRIMAL>(); // Building alpha_G0_1 Calculus::PrimalIdentity0 alpha_iG0 = a * Id0; // a * calculus.identity<0, PRIMAL>(); // a * invG0; //diag_alpha; Calculus::PrimalForm0 alpha_iG0_g = alpha_iG0 * g; // Builds a Laplacian but at distance 2 ! // const Matrix& M = tA_tS_S_A.myContainer; // for (int k = 0; k < M.outerSize(); ++k) // { // trace.info() << "-----------------------------------------------------------------------" << endl; // for ( Matrix::InnerIterator it( M, k ); it; ++it ) // trace.info() << "[" << it.row() << "," << it.col() << "] = " << it.value() << endl; // } // Building iG1_A_G0_tA_iG1 + tB_iG2_B // // A.tA // const Calculus::PrimalIdentity1 lap_operator_v = -1.0 * ( invG1 * primal_D0 * G0 * dual_h2 * dual_D1 * primal_h1 * invG1 ); // // tB.B // const Calculus::PrimalIdentity1 lap_operator_v = -1.0 * ( dual_h1 * dual_D0 * primal_h2 * invG2 * primal_D1 ); // tB.B + A.tA const Calculus::PrimalIdentity1 lap_operator_v = -1.0 * ( invG1 * primal_D0 * G0 * dual_h2 * dual_D1 * primal_h1 * invG1 + dual_h1 * dual_D0 * primal_h2 * invG2 * primal_D1 ); // SparseLU is so much faster than SparseQR // SimplicialLLT is much faster than SparseLU // typedef EigenLinearAlgebraBackend::SolverSparseQR LinearAlgebraSolver; // typedef EigenLinearAlgebraBackend::SolverSparseLU LinearAlgebraSolver; typedef EigenLinearAlgebraBackend::SolverSimplicialLLT LinearAlgebraSolver; typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, PRIMAL, 0, PRIMAL> SolverU; SolverU solver_u; typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 1, PRIMAL, 1, PRIMAL> SolverV; SolverV solver_v; while ( l1 >= l2 ) { trace.info() << "************ lambda = " << l1 << " **************" << endl; double l = l1; trace.info() << "B'B'" << endl; const Calculus::PrimalIdentity1 lBB = l * lap_operator_v; Calculus::PrimalForm1 l_sur_4( calculus ); for ( Calculus::Index index = 0; index < l_sur_4.myContainer.rows(); index++) l_sur_4.myContainer( index ) = l/4.0; l_sur_4 = Id1 * l_sur_4; //tS_S * l_sur_4; // double epsilon = 0.0; for( int i = 0; i < 2; ++i ) // change alpha { g = u; alpha_iG0 = a * Id0; alpha_iG0_g = alpha_iG0 * g; for ( double eps = e1; eps >= e2; eps /= er ) { Calculus::PrimalIdentity1 BB = eps * lBB + ( l/(4.0*eps) ) * Id1; // tS_S; int i = 0; for ( ; i < n; ++i ) { trace.info() << "------ Iteration " << i << "/" << n << " ------" << endl; trace.beginBlock("Solving for u"); trace.info() << "Building matrix Av2A" << endl; //double tvtSSv = 0.0; Calculus::PrimalIdentity1 diag_v = diag( calculus, v ); Calculus::PrimalDerivative0 v_A = diag_v * primal_D0; Calculus::PrimalIdentity0 Av2A = square( calculus, v_A ) + alpha_iG0; trace.info() << "Prefactoring matrix Av2A := tA_v_tv_A + alpha_iG0" << endl; trace.info() << "-------------------------------------------------------------------------------" << endl; // const Matrix& M = Av2A.myContainer; // for (int k = 0; k < M.outerSize(); ++k) // for ( Matrix::InnerIterator it( M, k ); it; ++it ) // trace.info() << "[" << it.row() << "," << it.col() << "] = " << it.value() << endl; solver_u.compute( Av2A ); trace.info() << "Solving Av2A u = ag" << endl; u = solver_u.solve( alpha_iG0_g ); trace.info() << ( solver_u.isValid() ? "OK" : "ERROR" ) << " " << solver_u.myLinearAlgebraSolver.info() << endl; trace.info() << "-------------------------------------------------------------------------------" << endl; trace.endBlock(); const Calculus::PrimalForm1 former_v = v; trace.beginBlock("Solving for v"); trace.info() << "Building matrix BB+Mw2" << endl; const Calculus::PrimalIdentity1 A_u = diag( calculus, primal_D0 * u ); const Calculus::PrimalIdentity1 tu_tA_A_u = square( calculus, A_u ); solver_v.compute( tu_tA_A_u + BB ); v = solver_v.solve( (1.0/eps) * l_sur_4 ); trace.info() << ( solver_v.isValid() ? "OK" : "ERROR" ) << " " << solver_v.myLinearAlgebraSolver.info() << endl; trace.endBlock(); trace.beginBlock("Checking v, computing norms"); double m1 = 1.0; double m2 = 0.0; double ma = 0.0; for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++) { double val = v.myContainer( index ); m1 = std::min( m1, val ); m2 = std::max( m2, val ); ma += val; } trace.info() << "1-form v: min=" << m1 << " avg=" << ( ma/ v.myContainer.rows() ) << " max=" << m2 << std::endl; for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++) v.myContainer( index ) = std::min( std::max(v.myContainer( index ), 0.0) , 1.0 ); double n_infty = 0.0; double n_2 = 0.0; double n_1 = 0.0; for ( Calculus::Index index = 0; index < v.myContainer.rows(); index++) { n_infty = max( n_infty, fabs( v.myContainer( index ) - former_v.myContainer( index ) ) ); n_2 += ( v.myContainer( index ) - former_v.myContainer( index ) ) * ( v.myContainer( index ) - former_v.myContainer( index ) ); n_1 += fabs( v.myContainer( index ) - former_v.myContainer( index ) ); } n_1 /= v.myContainer.rows(); n_2 = sqrt( n_2 / v.myContainer.rows() ); trace.info() << "Variation |v^k+1 - v^k|_oo = " << n_infty << endl; trace.info() << "Variation |v^k+1 - v^k|_2 = " << n_2 << endl; trace.info() << "Variation |v^k+1 - v^k|_1 = " << n_1 << endl; trace.endBlock(); if ( n_infty < 1e-4 ) break; } epsilon = eps; } a = 0.5; } // affichage des energies ******************************************************************** trace.beginBlock("Computing energies"); // a(u-g)^2 Calculus::PrimalIdentity0 diag_alpha = a * Id0; const Calculus::PrimalForm0 u_minus_g = u - g; double alpha_square_u_minus_g = innerProduct( calculus, diag_alpha * u_minus_g, u_minus_g ); trace.info() << "- a(u-g)^2 = " << alpha_square_u_minus_g << std::endl; // v^2|grad u|^2 const Calculus::PrimalIdentity1 diag_v = diag( calculus, v ); const Calculus::PrimalForm1 v_A_u = diag_v * primal_D0 * u; double square_v_grad_u = innerProduct( calculus, v_A_u, v_A_u ); trace.info() << "- v^2|grad u|^2 = " << square_v_grad_u << std::endl; // // JOL: 1000 * plus rapide ! // trace.info() << " - u^t N u" << std::endl; // Calculus::PrimalForm0 u_prime = Av2A * u; // for ( Calculus::Index index = 0; index < u.myContainer.rows(); index++) // V2gradU2 += u.myContainer( index ) * u_prime.myContainer( index ); // // for ( Calculus::Index index_i = 0; index_i < u.myContainer.rows(); index_i++) // // for ( Calculus::Index index_j = 0; index_j < u.myContainer.rows(); index_j++) // // V2gradU2 += u.myContainer( index_i ) * Av2A.myContainer.coeff( index_i,index_j ) * u.myContainer( index_j ) ; // // le|grad v|^2 Calculus::PrimalForm1 v_prime = lap_operator_v * v; double le_square_grad_v = l * epsilon * innerProduct( calculus, v, v_prime ); trace.info() << "- le|grad v|^2 = " << le_square_grad_v << std::endl; // l(1-v)^2/4e Calculus::PrimalForm1 one_minus_v = v; for ( Calculus::Index index_i = 0; index_i < v.myContainer.rows(); index_i++) one_minus_v.myContainer( index_i ) = 1.0 - one_minus_v.myContainer( index_i ); double l_over_4e_square_1_minus_v = l / (4*epsilon) * innerProduct( calculus, one_minus_v, one_minus_v ); trace.info() << "- l(1-v)^2/4e = " << l_over_4e_square_1_minus_v << std::endl; // l.per double Lper = 2.0* l_over_4e_square_1_minus_v; //le_square_grad_v + l_over_4e_square_1_minus_v; trace.info() << "- l.per = " << Lper << std::endl; // AT tot double ATtot = alpha_square_u_minus_g + square_v_grad_u + Lper; // f << "l " << " a " << " e " << " a(u-g)^2 " << " v^2|grad u|^2 " << " le|grad v|^2 " << " l(1-v)^2/4e " << " l.per " << " AT tot"<< endl; f << tronc(l,8) << "\t" << a << "\t" << tronc(epsilon,4) << "\t" << tronc(alpha_square_u_minus_g,5) << "\t" << tronc(square_v_grad_u,5) << "\t" << tronc(le_square_grad_v,5) << "\t" << tronc(l_over_4e_square_1_minus_v,5) << "\t" << tronc(Lper,5) << "\t" << tronc(ATtot,5) << endl; trace.endBlock(); // *********************************************************************************************************************** int int_l = (int) floor(l); int dec_l = (int) (floor((l-floor(l))*10000000)); ostringstream ossU; ossU << boost::format("%s-l%.7f-u.pgm") %f2 %l; string str_image_u = ossU.str(); savePrimalForm0ToImage( calculus, end_image, u, str_image_u); ostringstream ossV; ossV << boost::format("%s-l%.7f-v.pgm") %f2 %l; string str_image_v = ossV.str(); savePrimalForm1ToImage( calculus, dbl_image, v, str_image_v ); l1 /= lr; } f.close(); return 0; }
int main( int argc, char** argv ) { // parse command line ---------------------------------------------- po::options_description general_opt("Allowed options are"); general_opt.add_options() ("help,h", "display this message") ("input,i", po::value< std::string >(), ".vol file") ("trivial-radius,t", po::value<double>()->default_value( 3 ), "the parameter t defining the radius for the Trivial estimator, which is used for reorienting II or VCM normal estimations." ) ("r-radius,r", po::value< double >(), "Kernel radius r for IntegralInvariant estimator" ) ("noise,k", po::value< double >()->default_value(0.5), "Level of Kanungo noise ]0;1[" ) ("min,l", po::value< int >()->default_value(0), "set the minimal image threshold to define the image object (object defined by the voxel with intensity belonging to ]min, max ] )." ) ("max,u", po::value< int >()->default_value(255), "set the minimal image threshold to define the image object (object defined by the voxel with intensity belonging to ]min, max] )." ) ("lambda,L", po::value<double>()->default_value( 0.05 ), "the parameter lambda of AT functional." ) ("alpha,a", po::value<double>()->default_value( 0.1 ), "the parameter alpha of AT functional." ) ("epsilon,e", po::value<double>()->default_value( 4.0 ), "the initial parameter epsilon of AT functional." ); bool parseOK = true; po::variables_map vm; try { po::store( po::parse_command_line( argc, argv, general_opt ), vm ); } catch( const std::exception & ex ) { parseOK = false; trace.error() << " Error checking program options: " << ex.what() << std::endl; } bool neededArgsGiven=true; if (parseOK && !(vm.count("input"))){ missingParam("--input"); neededArgsGiven=false; } if (parseOK && !(vm.count("r-radius"))){ missingParam("--r-radius"); neededArgsGiven=false; } double noiseLevel = vm["noise"].as< double >(); if( noiseLevel < 0.0 || noiseLevel > 1.0 ) { parseOK = false; trace.error() << "The noise level should be in the interval: [0, 1]"<< std::endl; } if(!neededArgsGiven || !parseOK || vm.count("help") || argc <= 1 ) { trace.info()<< "Vol file viewer, with normals regularized by Ambrosio-Tortorelli functionnal" <<std::endl << general_opt << "\n" << "Basic usage: "<<std::endl << "\t at-3d-normals -i file.vol -r 5 --noise 0.1"<<std::endl << std::endl; return 0; } QApplication application(argc,argv); int min = vm["min"].as< int >(); int max = vm["max"].as< int >(); const double h = 1.0; // not pertinent for now. //----------------------------------------------------------------------------- // Types. typedef Z3i::Space Space; typedef Z3i::KSpace KSpace; typedef Space::RealVector RealVector; typedef KSpace::SCell SCell; typedef KSpace::Cell Cell; typedef KSpace::Surfel Surfel; typedef Z3i::Domain Domain; typedef ImageSelector<Domain, unsigned char>::Type Image; typedef IntervalForegroundPredicate< Image > Object; typedef KanungoNoise< Object, Domain > KanungoPredicate; typedef BinaryPointPredicate<DomainPredicate<Domain>, KanungoPredicate, AndBoolFct2 > NoisyObject; typedef KSpace::SurfelSet SurfelSet; typedef SetOfSurfels< KSpace, SurfelSet > MySetOfSurfels; typedef DigitalSurface< MySetOfSurfels > MyDigitalSurface; typedef MyDigitalSurface::ConstIterator ConstIterator; //----------------------------------------------------------------------------- // Loading vol file. trace.beginBlock( "Loading vol file." ); std::string inputFile = vm[ "input" ].as< std::string >(); std::string extension = inputFile.substr(inputFile.find_last_of(".") + 1); if(extension!="vol" && extension != "p3d" && extension != "pgm3D" && extension != "pgm3d" && extension != "sdp" && extension != "pgm" ) { trace.info() << "File extension not recognized: "<< extension << std::endl; return 0; } Image image = GenericReader<Image>::import (inputFile ); trace.endBlock(); //----------------------------------------------------------------------------- // Extracting object with possible noise. trace.beginBlock( "Extracting object with possible noise." ); Object object( image, min, max ); KanungoPredicate kanungo_pred( object, image.domain(), noiseLevel ); DomainPredicate<Domain> domain_pred( image.domain() ); AndBoolFct2 andF; NoisyObject noisy_object(domain_pred, kanungo_pred, andF ); Domain domain = image.domain(); KSpace K; bool space_ok = K.init( domain.lowerBound()-Z3i::Domain::Point::diagonal(), domain.upperBound()+Z3i::Domain::Point::diagonal(), true ); if (!space_ok) { trace.error() << "Error in the Khalimsky space construction."<<std::endl; return 2; } CanonicSCellEmbedder< KSpace > embedder( K ); SurfelAdjacency< KSpace::dimension > surfAdj( true ); trace.endBlock(); //----------------------------------------------------------------------------- //! [3dVolBoundaryViewer-ExtractingSurface] trace.beginBlock( "Extracting boundary by scanning the space. " ); MySetOfSurfels theSetOfSurfels( K, surfAdj ); Surfaces<KSpace>::sMakeBoundary( theSetOfSurfels.surfelSet(), K, image, domain.lowerBound(), domain.upperBound() ); MyDigitalSurface digSurf( theSetOfSurfels ); trace.info() << "Digital surface has " << digSurf.size() << " surfels." << std::endl; trace.endBlock(); //! [3dVolBoundaryViewer-ExtractingSurface] // Map surfel -> estimated normals. std::map<SCell,RealVector> n_estimations; //----------------------------------------------------------------------------- // Estimating orientation of normals trace.beginBlock( "Estimating orientation of normals by simple convolutions of trivial surfel normals." ); double t = vm["trivial-radius"].as<double>(); typedef RealVector::Component Scalar; typedef functors::HatFunction<Scalar> Functor; typedef functors::ElementaryConvolutionNormalVectorEstimator< Surfel, CanonicSCellEmbedder<KSpace> > SurfelFunctor; typedef ExactPredicateLpSeparableMetric<Space,2> Metric; typedef LocalEstimatorFromSurfelFunctorAdapter< MySetOfSurfels, Metric, SurfelFunctor, Functor> NormalEstimator; Functor fct( 1.0, t ); CanonicSCellEmbedder<KSpace> canonic_embedder( K ); SurfelFunctor surfelFct( canonic_embedder, 1.0 ); NormalEstimator nt_estimator; Metric aMetric; std::vector<RealVector> nt_estimations; nt_estimator.attach( digSurf ); nt_estimator.setParams( aMetric, surfelFct, fct, t ); nt_estimator.init( h, digSurf.begin(), digSurf.end()); nt_estimator.eval( digSurf.begin(), digSurf.end(), std::back_inserter( nt_estimations ) ); trace.endBlock(); //----------------------------------------------------------------------------- // Estimating normals trace.beginBlock( "Estimating normals with II." ); typedef typename Domain::ConstIterator DomainConstIterator; typedef functors::IINormalDirectionFunctor<Space> IINormalFunctor; typedef IntegralInvariantCovarianceEstimator<KSpace, NoisyObject, IINormalFunctor> IINormalEstimator; std::vector<RealVector> nii_estimations; const double r = vm["r-radius"].as<double>(); IINormalEstimator nii_estimator( K, noisy_object ); trace.info() << " r=" << r << std::endl; nii_estimator.setParams( r ); nii_estimator.init( h, digSurf.begin(), digSurf.end() ); nii_estimator.eval( digSurf.begin(), digSurf.end(), std::back_inserter( nii_estimations ) ); // Fix orientations of ii. for ( unsigned int i = 0; i < nii_estimations.size(); ++i ) { if ( nii_estimations[ i ].dot( nt_estimations[ i ] ) < 0.0 ) nii_estimations[ i ] *= -1.0; } trace.info() << "- nb estimations = " << nii_estimations.size() << std::endl; trace.endBlock(); // The chosen estimator is II. { unsigned int i = 0; for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it, ++i ) { RealVector nii = nii_estimations[ i ]; nii /= nii.norm(); n_estimations[ *it ] = nii; } } //----------------------------------------------------------------------------- //! [3dVolBoundaryViewer-ViewingSurface] trace.beginBlock( "Displaying everything. " ); Viewer3D<Space,KSpace> viewer( K ); viewer.setWindowTitle("Simple boundary of volume Viewer"); viewer.show(); viewer << SetMode3D(K.unsigns( *(digSurf.begin()) ).className(), "Basic"); unsigned int i = 0; for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it, ++i ) { viewer.setFillColor( Color( 200, 200, 250 ) ); Display3DFactory<Space,KSpace>::drawOrientedSurfelWithNormal( viewer, *it, n_estimations[ *it ], false ); } viewer << Viewer3D<>::updateDisplay; trace.endBlock(); //----------------------------------------------------------------------------- // Defining Discrete Calculus. typedef CubicalComplex< KSpace > CComplex; typedef DiscreteExteriorCalculus<2,3, EigenLinearAlgebraBackend> Calculus; typedef Calculus::Index Index; typedef Calculus::PrimalForm0 PrimalForm0; typedef Calculus::PrimalForm1 PrimalForm1; typedef Calculus::PrimalForm2 PrimalForm2; typedef Calculus::PrimalIdentity0 PrimalIdentity0; typedef Calculus::PrimalIdentity1 PrimalIdentity1; typedef Calculus::PrimalIdentity2 PrimalIdentity2; trace.beginBlock( "Creating Discrete Exterior Calculus. " ); Calculus calculus; calculus.initKSpace<Domain>( domain ); const KSpace& Kc = calculus.myKSpace; // should not be used. // Use a cubical complex to find all lower incident cells easily. CComplex complex( K ); for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it ) complex.insertCell( 2, K.unsigns( *it ) ); complex.close(); for ( CComplex::CellMapIterator it = complex.begin( 0 ), itE = complex.end( 0 ); it != itE; ++it ) calculus.insertSCell( K.signs( it->first, K.POS ) ); for ( CComplex::CellMapIterator it = complex.begin( 1 ), itE = complex.end( 1 ); it != itE; ++it ) { SCell linel = K.signs( it->first, K.POS ); Dimension k = * K.sDirs( linel ); bool pos = K.sDirect( linel, k ); calculus.insertSCell( pos ? linel : K.sOpp( linel ) ); // calculus.insertSCell( K.signs( it->first, K.POS ) ); } // for ( CComplex::CellMapIterator it = complex.begin( 2 ), itE = complex.end( 2 ); it != itE; ++it ) // calculus.insertSCell( K.signs( it->first, K.POS ) ); for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it ) { calculus.insertSCell( *it ); // SCell surfel = *it; // Dimension k = K.sOrthDir( surfel ); // bool pos = K.sDirect( surfel, k ); // calculus.insertSCell( pos ? *it : K.sOpp( *it ) ); } calculus.updateIndexes(); trace.info() << calculus << endl; std::vector<PrimalForm2> g; g.reserve( 3 ); g.push_back( PrimalForm2( calculus ) ); g.push_back( PrimalForm2( calculus ) ); g.push_back( PrimalForm2( calculus ) ); Index nb2 = g[ 0 ].myContainer.rows(); for ( Index index = 0; index < nb2; index++) { const Calculus::SCell& cell = g[ 0 ].getSCell( index ); if ( theSetOfSurfels.isInside( cell ) ) { const RealVector& n = n_estimations[ cell ]; g[ 0 ].myContainer( index ) = n[ 0 ]; g[ 1 ].myContainer( index ) = n[ 1 ]; g[ 2 ].myContainer( index ) = n[ 2 ]; } else { const RealVector& n = n_estimations[ K.sOpp( cell ) ]; g[ 0 ].myContainer( index ) = n[ 0 ]; g[ 1 ].myContainer( index ) = n[ 1 ]; g[ 2 ].myContainer( index ) = n[ 2 ]; } } cout << endl; trace.info() << "primal_D0" << endl; const Calculus::PrimalDerivative0 primal_D0 = calculus.derivative<0,PRIMAL>(); trace.info() << "primal_D1" << endl; const Calculus::PrimalDerivative1 primal_D1 = calculus.derivative<1,PRIMAL>(); trace.info() << "dual_D0" << endl; const Calculus::DualDerivative0 dual_D0 = calculus.derivative<0,DUAL>(); trace.info() << "dual_D1" << endl; const Calculus::DualDerivative1 dual_D1 = calculus.derivative<1,DUAL>(); trace.info() << "primal_h0" << endl; const Calculus::PrimalHodge0 primal_h0 = calculus.hodge<0,PRIMAL>(); trace.info() << "primal_h1" << endl; const Calculus::PrimalHodge1 primal_h1 = calculus.hodge<1,PRIMAL>(); trace.info() << "primal_h2" << endl; const Calculus::PrimalHodge2 primal_h2 = calculus.hodge<2,PRIMAL>(); trace.info() << "dual_h1" << endl; const Calculus::DualHodge1 dual_h1 = calculus.hodge<1,DUAL>(); trace.info() << "dual_h2" << endl; const Calculus::DualHodge2 dual_h2 = calculus.hodge<2,DUAL>(); trace.endBlock(); //----------------------------------------------------------------------------- // Building AT functional. trace.beginBlock( "Building AT functional. " ); double a = vm[ "alpha" ].as<double>(); double e = vm[ "epsilon" ].as<double>(); double l = vm[ "lambda" ].as<double>(); // u = g at the beginning trace.info() << "u[0,1,2]" << endl; std::vector<PrimalForm2> u; u.push_back( g[ 0 ] ); u.push_back( g[ 1 ] ); u.push_back( g[ 2 ] ); // v = 1 at the beginning trace.info() << "v" << endl; PrimalForm1 v( calculus ); Index nb1 = v.myContainer.rows(); for ( Index index = 0; index < nb1; index++) v.myContainer( index ) = 1.0; const PrimalIdentity0 Id0 = calculus.identity<0, PRIMAL>(); const PrimalIdentity1 Id1 = calculus.identity<1, PRIMAL>(); const PrimalIdentity2 Id2 = calculus.identity<2, PRIMAL>(); // Building alpha_ trace.info() << "alpha_g" << endl; const PrimalIdentity2 alpha_Id2 = a * Id2; // a * invG0; vector<PrimalForm2> alpha_g; alpha_g.push_back( alpha_Id2 * g[ 0 ] ); alpha_g.push_back( alpha_Id2 * g[ 1 ] ); alpha_g.push_back( alpha_Id2 * g[ 2 ] ); trace.info() << "lap_operator_v" << endl; const PrimalIdentity1 lap_operator_v = -1.0 * ( primal_D0 * dual_h2 * dual_D1 * primal_h1 + dual_h1 * dual_D0 * primal_h2 * primal_D1 ); // SparseLU is so much faster than SparseQR // SimplicialLLT is much faster than SparseLU // typedef EigenLinearAlgebraBackend::SolverSparseQR LinearAlgebraSolver; // typedef EigenLinearAlgebraBackend::SolverSparseLU LinearAlgebraSolver; typedef EigenLinearAlgebraBackend::SolverSimplicialLLT LinearAlgebraSolver; typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 2, PRIMAL, 2, PRIMAL> SolverU; SolverU solver_u; typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 1, PRIMAL, 1, PRIMAL> SolverV; SolverV solver_v; trace.info() << "lB'B'" << endl; const PrimalIdentity1 lBB = l * lap_operator_v; PrimalForm1 l_sur_4( calculus ); for ( Index index = 0; index < nb1; index++) l_sur_4.myContainer( index ) = l / 4.0; double coef_eps = 2.0; double eps = 2.0 * e; const int n = 10; trace.endBlock(); //----------------------------------------------------------------------------- // Solving AT functional. trace.beginBlock( "Solving AT functional. " ); while ( eps / coef_eps >= h ) { eps /= coef_eps; trace.info() << "************** epsilon = " << eps << "***************************************" << endl; const PrimalIdentity1 BB = eps * lBB + ( l/(4.0*eps) ) * Id1; // tS_S; for ( int i = 0; i < n; ++i ) { trace.info() << "---------- Iteration " << i << "/" << n << " ------------------------------" << endl; trace.info() << "-------------------------------------------------------------------------------" << endl; trace.beginBlock("Solving for u"); trace.info() << "Building matrix Av2A" << endl; PrimalIdentity1 diag_v = diag( calculus, v ); PrimalIdentity2 U_Id2 = -1.0 * primal_D1 * diag_v * diag_v * dual_h1 * dual_D0 * primal_h2 + alpha_Id2; trace.info() << "Prefactoring matrix Av2A + alpha_iG0" << endl; solver_u.compute( U_Id2 ); for ( unsigned int d = 0; d < 3; ++d ) { trace.info() << "Solving (Av2A + alpha_iG0) u[" << d << "] = ag[" << d << "]" << endl; u[ d ] = solver_u.solve( alpha_g[ d ] ); trace.info() << " => " << ( solver_u.isValid() ? "OK" : "ERROR" ) << " " << solver_u.myLinearAlgebraSolver.info() << endl; } trace.info() << "-------------------------------------------------------------------------------" << endl; trace.endBlock(); trace.beginBlock("Solving for v"); const PrimalForm1 former_v = v; trace.info() << "Building matrix tu_tA_A_u + BB + Mw2" << endl; PrimalIdentity1 V_Id1 = BB; for ( unsigned int d = 0; d < 3; ++d ) { const PrimalIdentity1 A_u = diag( calculus, dual_h1 * dual_D0 * primal_h2 * u[ d ] ); V_Id1.myContainer += square( calculus, A_u ).myContainer; } trace.info() << "Prefactoring matrix tu_tA_A_u + BB + Mw2" << endl; solver_v.compute( V_Id1 ); trace.info() << "Solving (tu_tA_A_u + BB + Mw2) v = 1/(4eps) * l" << endl; v = solver_v.solve( (1.0/eps) * l_sur_4 ); trace.info() << " => " << ( solver_v.isValid() ? "OK" : "ERROR" ) << " " << solver_v.myLinearAlgebraSolver.info() << endl; trace.info() << "-------------------------------------------------------------------------------" << endl; trace.endBlock(); for ( Index index = 0; index < nb2; index++) { double n2 = 0.0; for ( unsigned int d = 0; d < 3; ++d ) n2 += u[ d ].myContainer( index ) * u[ d ].myContainer( index ); double norm = sqrt( n2 ); for ( unsigned int d = 0; d < 3; ++d ) u[ d ].myContainer( index ) /= norm; } trace.beginBlock("Checking v, computing norms"); double m1 = 1.0; double m2 = 0.0; double ma = 0.0; for ( Index index = 0; index < nb1; index++) { double val = v.myContainer( index ); m1 = std::min( m1, val ); m2 = std::max( m2, val ); ma += val; } trace.info() << "1-form v: min=" << m1 << " avg=" << ( ma / nb1 ) << " max=" << m2 << std::endl; for ( Index index = 0; index < nb1; index++) v.myContainer( index ) = std::min( std::max(v.myContainer( index ), 0.0) , 1.0 ); double n_infty = 0.0; double n_2 = 0.0; double n_1 = 0.0; for ( Index index = 0; index < nb1; index++) { n_infty = std::max( n_infty, fabs( v.myContainer( index ) - former_v.myContainer( index ) ) ); n_2 += ( v.myContainer( index ) - former_v.myContainer( index ) ) * ( v.myContainer( index ) - former_v.myContainer( index ) ); n_1 += fabs( v.myContainer( index ) - former_v.myContainer( index ) ); } n_1 /= v.myContainer.rows(); n_2 = sqrt( n_2 / v.myContainer.rows() ); trace.info() << "Variation |v^k+1 - v^k|_oo = " << n_infty << endl; trace.info() << "Variation |v^k+1 - v^k|_2 = " << n_2 << endl; trace.info() << "Variation |v^k+1 - v^k|_1 = " << n_1 << endl; trace.endBlock(); if ( n_infty < 1e-4 ) break; } // for ( int i = 0; i < n; ++i ) } trace.endBlock(); //----------------------------------------------------------------------------- // Displaying regularized normals trace.beginBlock( "Displaying regularized normals. " ); Viewer3D<Space,KSpace> viewerR( K ); viewerR.setWindowTitle("Regularized normals"); viewerR.show(); viewerR << SetMode3D(K.unsigns( *(digSurf.begin()) ).className(), "Basic"); viewerR.setFillColor( Color( 200, 200, 250 ) ); for ( Index index = 0; index < nb2; index++) { const SCell& cell = u[ 0 ].getSCell( index ); // const RealVector& n = n_estimations[ cell ]; RealVector nr = RealVector( u[ 0 ].myContainer( index ), u[ 1 ].myContainer( index ), u[ 2 ].myContainer( index ) ); nr /= nr.norm(); if ( theSetOfSurfels.isInside( cell ) ) Display3DFactory<Space,KSpace>::drawOrientedSurfelWithNormal( viewerR, cell, nr, false ); else Display3DFactory<Space,KSpace>::drawOrientedSurfelWithNormal( viewerR, K.sOpp( cell ), nr, false ); } viewerR.setLineColor( Color( 255, 0, 0 ) ); for ( Index index = 0; index < nb1; index++) { const SCell& cell = v.getSCell( index ); Dimension k = * K.sDirs( cell ); const SCell p0 = K.sIncident( cell, k, true ); const SCell p1 = K.sIncident( cell, k, false ); if ( v.myContainer( index ) >= 0.5 ) continue; viewerR.addLine( embedder.embed( p0 ), embedder.embed( p1 ), (0.5 - v.myContainer( index ))/ 5.0 ); } viewerR << Viewer3D<>::updateDisplay; trace.endBlock(); return application.exec(); }