DGtalTools 2.0.0
Loading...
Searching...
No Matches
atv-u2-v0.cpp
1
32#include <iostream>
33
34#include <sstream>
35#include <string>
36#include <functional>
37#include <boost/format.hpp>
38
39#include "CLI11.hpp"
40
41#include "DGtal/base/Common.h"
42#include "DGtal/helpers/StdDefs.h"
43#include "DGtal/io/readers/GenericReader.h"
44#include "DGtal/io/writers/GenericWriter.h"
45
46#include "ATVu2v0.h"
47
210using namespace std;
211using namespace DGtal;
212
213int main( int argc, char* argv[] )
214{
215 using namespace Z2i;
216
217 // parse command line ----------------------------------------------
218 CLI::App app;
219 string f1;
220 string f2 {"AT"};
221 string inpainting_mask;
222 double l;
223 double l1 {0.3125};
224 double l2 {0.0005};
225 double lr {sqrt(2)};
226 double a {1.0};
227 double epsilon;
228
229 double e1 {2.0};
230 double e2 {0.25};
231 double er {2.0};
232 int verb {0};
233 int nbiter = {10};
234 int pix_sz = {1};
235 string scv {"0xff0000"};
236 string isnr;
237 bool metric;
238
239
240 stringstream ssDescr;
241 ssDescr << "Usage: " << argv[0] << " -i toto.pgm\n"
242 << "Computes a variant of the Ambrosio-Tortorelli reconstruction/segmentation of an input image."
243 << "It outputs 2 or 3 images (of basename given by option --output) giving the"
244 << " reconstructed image u, and other images superposing u and the discontinuities v."
245 << endl << endl
246 << " / "
247 << endl
248 << " | a.(u-g)^2 + v^2 |grad u|^2 + c.l.e^3.|Delta v|^2 + (c.l/e).(1-v)^2 "
249 << endl
250 << " / "
251 << endl
252 << "Discretized as (u 2-form, v 0-form, L=A^t A laplacian, B edge-face bdy, M vertex-edge average)" << endl
253 << "E(u,v) = a(u-g)^t (u-g) + u^t B diag(M v)^2 B^t u + l e^3 v^t L^t L v + l/e (1-v)^t (1-v)" << endl
254 << endl
255<< "where c=1/(2sqrt(2)) is a constant that reflects the fact that the Gamma-limit of discontinuity length estimation term is 2.sqrt(2).L, for a length L of discontinuities."
256 << "Example: ./atv-u2-v0 -i ../Images/cerclesTriangle64b02.pgm -o tmp -a 0.05 -e 1 --lambda-1 0.1 --lambda-2 0.00001"
257 << endl;
258
259
260 app.description(ssDescr.str());
261
262
263app.add_option("-i,--input,1", f1, "the input image PPM filename." )
264 ->required()
265 ->check(CLI::ExistingFile);
266app.add_option("--inpainting-mask,-m", inpainting_mask, "the input inpainting mask filename." );
267app.add_option("--output,-o", f2, "the output image basename.");
268auto lambdaOpt = app.add_option("--lambda,-l",l, "the parameter lambda.");
269app.add_flag("--metric-average,-M",metric, "use metric average to smooth L1-metric." );
270
271app.add_option("--lambda-1,-1",l1, "the initial parameter lambda (l1).");
272app.add_option("--lambda-2,-2",l2, "the final parameter lambda (l2)." );
273app.add_option("--lambda-ratio,-q",lr, "the division ratio for lambda from l1 to l2.");
274app.add_option("--alpha,-a",a, "the parameter alpha.");
275auto epsOpt = app.add_option("--epsilon,-e", "the initial and final parameter epsilon of AT functional at the same time.");
276
277app.add_option("--epsilon-1",e1, "the initial parameter epsilon.");
278app.add_option("--epsilon-2",e2, "the final parameter epsilon.");
279app.add_option("--epsilon-r",er, "sets the ratio between two consecutive epsilon values of AT functional.");
280
281app.add_option("--nbiter,-n",nbiter, "the maximum number of iterations." );
282auto snrOpt = app.add_option("--image-snr", isnr, "the input image without deterioration if you wish to compute the SNR.");
283app.add_option("--pixel-size,-p", pix_sz, "the pixel size for outputing images (useful when one wants to see the discontinuities v on top of u).");
284app.add_option("--color-v,-c",scv, "the color chosen for displaying the singularities v (e.g. red is 0xff0000)." );
285app.add_option("--verbose,-v", verb, "the verbose level (0: silent, 1: less silent, etc)." );
286
287app.get_formatter()->column_width(40);
288CLI11_PARSE(app, argc, argv);
289// END parse comm"metric-average,Mand line using CLI ----------------------------------------------
290
291
292 Color color_v( (unsigned int) std::stoul( scv, nullptr, 16 ), 255 );
293 if ( lambdaOpt->count()) l1 = l2 = l;
294 if ( l2 > l1 ) l2 = l1;
295 if ( lr <= 1.0 ) lr = sqrt(2);
296 if ( epsOpt->count() > 0 ){
297 e1 = e2 = epsilon;
298
299 }
300 bool snr = snrOpt->count() > 0;
301
302
303 bool color_image = f1.size() > 4 && f1.compare( f1.size() - 4, 4, ".ppm" ) == 0;
304 bool grey_image = f1.size() > 4 && f1.compare( f1.size() - 4, 4, ".pgm" ) == 0;
305 if ( ! color_image && ! grey_image )
306 {
307 trace.error() << "Input image file must be either a PGM (grey-level) or a PPM (color) image with these extensions."
308 << endl;
309 return 2;
310 }
311
312
313 KSpace K;
314 ATVu2v0< KSpace > AT( verb );
315 Domain domain;
316 AT.setMetricAverage( metric );
317
318 typedef ATVu2v0<KSpace>::Calculus Calculus;
319 typedef ImageContainerBySTLVector<Domain, Color> ColorImage;
320 typedef ImageContainerBySTLVector<Domain, unsigned char> GreyLevelImage;
321 //---------------------------------------------------------------------------
322 if ( color_image )
323 {
324 trace.beginBlock("Reading PPM image");
325 ColorImage image = PPMReader<ColorImage>::importPPM( f1 );
326 trace.endBlock();
327 trace.beginBlock("Building AT");
328 domain = image.domain();
329 K.init( domain.lowerBound(), domain.upperBound(), true );
330 AT.init( K );
331 AT.addInput( image, [] ( Color c ) -> double { return ((double) c.red()) / 255.0; } );
332 AT.addInput( image, [] ( Color c ) -> double { return ((double) c.green()) / 255.0; } );
333 AT.addInput( image, [] ( Color c ) -> double { return ((double) c.blue()) / 255.0; } );
334 trace.endBlock();
335 }
336 else if ( grey_image )
337 {
338 trace.beginBlock("Reading PGM image");
339 GreyLevelImage image = PGMReader<GreyLevelImage>::importPGM( f1 );
340 trace.endBlock();
341 trace.beginBlock("Building AT");
342 domain = image.domain();
343 K.init( domain.lowerBound(), domain.upperBound(), true );
344 AT.init( K );
345 AT.addInput( image, [] (unsigned char c ) { return ((double) c) / 255.0; } );
346 trace.endBlock();
347 }
348
349 //---------------------------------------------------------------------------
350 if ( snr && color_image )
351 {
352 trace.beginBlock("Reading ideal PPM image");
353 ColorImage image = PPMReader<ColorImage>::importPPM( isnr );
354 trace.endBlock();
355 AT.addInput( image, [] ( Color c ) -> double { return ((double) c.red()) / 255.0; }, true );
356 AT.addInput( image, [] ( Color c ) -> double { return ((double) c.green()) / 255.0; }, true );
357 AT.addInput( image, [] ( Color c ) -> double { return ((double) c.blue()) / 255.0; }, true );
358 }
359 else if ( snr && grey_image )
360 {
361 trace.beginBlock("Reading ideal PGM image");
362 GreyLevelImage image = PGMReader<GreyLevelImage>::importPGM( isnr );
363 trace.endBlock();
364 AT.addInput( image, [] (unsigned char c ) { return ((double) c) / 255.0; }, true );
365 }
366
367 //---------------------------------------------------------------------------
368 // Prepare zoomed output domain
369 Domain out_domain( pix_sz * domain.lowerBound(),
370 pix_sz * domain.upperBound() + Point::diagonal( pix_sz ) );
371 //---------------------------------------------------------------------------
372 AT.setUFromInput();
373 double g_snr = snr ? AT.computeSNR() : 0.0;
374
375 if ( inpainting_mask.size() > 0 )
376 {
377 string fm = inpainting_mask;
378 trace.beginBlock("Reading inpainting mask");
379 GreyLevelImage mask = GenericReader<GreyLevelImage>::import( fm );
380 trace.endBlock();
381 Calculus::PrimalForm2 m( AT.calculus );
382 for ( Calculus::Index index = 0; index < m.myContainer.rows(); index++)
383 {
384 auto cell = m.getSCell( index );
385 double col = ((double) mask( K.sCoords( cell ) )) / 255.0;
386 m.myContainer( index ) = col > 0.0 ? 1.0 : 0.0;
387 }
388 AT.setAlpha( a, m );
389 AT.setUFromInputAndMask();
390 if ( grey_image )
391 {
392 ostringstream ossGM;
393 ossGM << boost::format("%s-g-mask.pgm") %f2;
394 GreyLevelImage image_mg( domain );
395 const Calculus::PrimalForm2 mg = functions::dec::diagonal( m ) * AT.getG( 0 );
397 ( AT.calculus, mg, image_mg, 0.0, 1.0, 1 );
398 PGMWriter<GreyLevelImage>::exportPGM( ossGM.str(), image_mg );
399 }
400 else if ( color_image )
401 {
402 ostringstream ossGM;
403 ossGM << boost::format("%s-g-mask.ppm") %f2;
404 ColorImage image_mg( domain );
405 const Calculus::PrimalForm2 mg0 = functions::dec::diagonal( m ) * AT.getG( 0 );
406 const Calculus::PrimalForm2 mg1 = functions::dec::diagonal( m ) * AT.getG( 1 );
407 const Calculus::PrimalForm2 mg2 = functions::dec::diagonal( m ) * AT.getG( 2 );
409 ( AT.calculus, mg0, mg1, mg2, image_mg, 0.0, 1.0, 1 );
410 PPMWriter<ColorImage, functors::Identity >::exportPPM( ossGM.str(), image_mg );
411 }
412 }
413 else
414 AT.setAlpha( a );
415
416 trace.info() << AT << std::endl;
417 double n_v = 0.0;
418 double eps = 0.0;
419 double cst = 1.0 / ( 2.0 * sqrt(2.0) );
420 while ( l1 >= l2 )
421 {
422 trace.info() << "************ lambda = " << l1 << " **************" << endl;
423 AT.setLambda( cst * l1 );
424 for ( eps = e1; eps >= e2; eps /= er )
425 {
426 trace.info() << " ======= epsilon = " << eps << " ========" << endl;
427 AT.setEpsilon( eps );
428 int n = 0;
429 do {
430 trace.progressBar( n, nbiter );
431 AT.solveU();
432 AT.solveV();
433 AT.checkV();
434 n_v = AT.computeVariation();
435 } while ( ( n_v > 0.0001 ) && ( ++n < nbiter ) );
436 trace.progressBar( n, nbiter );
437 trace.info() << "[#### last variation = " << n_v << " " << endl;
438 }
439 if ( grey_image )
440 {
441 if ( verb > 0 ) trace.beginBlock("Writing u[0] as PGM image");
442 ostringstream ossU, ossV, ossW;
443 ossU << boost::format("%s-a%.5f-l%.7f-u.pgm") % f2 % a % l1;
444 ossV << boost::format("%s-a%.5f-l%.7f-u-v.pgm") % f2 % a % l1;
445 ossW << boost::format("%s-a%.5f-l%.7f-u-v.ppm") % f2 % a % l1;
446 const Calculus::PrimalForm2 u = AT.getU( 0 );
447 const Calculus::PrimalForm1 v = AT.M01 * AT.getV();
448 // Restored image
449 GreyLevelImage image_u( domain );
451 ( AT.calculus, u, image_u, 0.0, 1.0, 1 );
452 PGMWriter<GreyLevelImage>::exportPGM( ossU.str(), image_u );
453 // Zoomed restored image with discontinuities (in black).
454 GreyLevelImage image_uv( out_domain );
456 ( AT.calculus, u, image_uv, 0.0, 1.0, pix_sz );
458 ( AT.calculus, v, image_uv, 0.0, 1.0, pix_sz );
459 PGMWriter<GreyLevelImage>::exportPGM( ossV.str(), image_uv );
460 // Zoomed restored image with discontinuities (in specified color).
461 ColorImage cimage( out_domain );
463 ( AT.calculus, u, u, u, cimage, 0.0, 1.0, pix_sz );
465 ( AT.calculus, v, cimage, color_v, 0.0, 1.0, pix_sz );
466 PPMWriter<ColorImage, functors::Identity >::exportPPM( ossW.str(), cimage );
467 if ( verb > 0 ) trace.endBlock();
468 }
469 else if ( color_image )
470 {
471 if ( verb > 0 ) trace.beginBlock("Writing u[0,1,2] as PGM image");
472 ostringstream ossU, ossV;
473 ossU << boost::format("%s-a%.5f-l%.7f-u.ppm") % f2 % a % l1;
474 ossV << boost::format("%s-a%.5f-l%.7f-u-v.ppm") % f2 % a % l1;
475 const Calculus::PrimalForm2 u0 = AT.getU( 0 );
476 const Calculus::PrimalForm2 u1 = AT.getU( 1 );
477 const Calculus::PrimalForm2 u2 = AT.getU( 2 );
478 const Calculus::PrimalForm1 v = AT.M01 * AT.getV();
479 // Restored image
480 ColorImage image_u( domain );
482 ( AT.calculus, u0, u1, u2, image_u, 0.0, 1.0, 1 );
483 PPMWriter<ColorImage, functors::Identity >::exportPPM( ossU.str(), image_u );
484 ColorImage image_uv( out_domain );
486 ( AT.calculus, u0, u1, u2, image_uv, 0.0, 1.0, pix_sz );
488 ( AT.calculus, v, image_uv, color_v, 0.0, 1.0, pix_sz );
489 PPMWriter<ColorImage, functors::Identity >::exportPPM( ossV.str(), image_uv );
490 if ( verb > 0 ) trace.endBlock();
491 }
492 // Compute SNR if possible
493 if ( snr )
494 {
495 double u_snr = AT.computeSNR();
496 trace.info() << "- SNR of u = " << u_snr << " SNR of g = " << g_snr << endl;
497 }
498 l1 /= lr;
499 }
500 return 0;
501}
void primalForm1ToGreyLevelImage(const Calculus &calculus, const typename Calculus::PrimalForm1 &v, Image &image, double cut_low=0.0, double cut_up=1.0, int pixel_size=1)
void form2ToGreyLevelImage(const Calculus &calculus, const AnyForm2 &u, Image &image, double cut_low=0.0, double cut_up=1.0, int pixel_size=1)
void threeForms2ToRGBColorImage(const Calculus &calculus, const AnyForm2 &u0, const AnyForm2 &u1, const AnyForm2 &u2, Image &image, double cut_low=0.0, double cut_up=1.0, int pixel_size=1)
void primalForm1ToRGBColorImage(const Calculus &calculus, const typename Calculus::PrimalForm1 &v, Image &image, Color color, double cut_low=0.0, double cut_up=1.0, int pixel_size=1)
DGtal::LinearOperator< Calculus, dim, duality, dim, duality > diagonal(const DGtal::KForm< Calculus, dim, duality > &kform)
Definition ATu0v1.h:57
Aim: This class solves a variant of Ambrosio-Tortorelli functional in a plane for u a (vector of) 2-f...
Definition ATVu2v0.h:78
DiscreteExteriorCalculus< 2, 2, LinearAlgebra > Calculus