DGtalTools 2.1.0
Loading...
Searching...
No Matches
at-u0-v1.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 "ATu0v1.h"
47
186using namespace std;
187using namespace DGtal;
188
189int main( int argc, char* argv[] )
190{
191 using namespace Z2i;
192
193
194 // parse command line using CLI ----------------------------------------------
195 CLI::App app;
196 string f1;
197 string f2 {"AT"};
198 string inpainting_mask;
199 double l;
200 double l1 {0.3125};
201 double l2 {0.0005};
202 double lr {sqrt(2)};
203 double a {1.0};
204 double epsilon;
205
206 double e1 {2.0};
207 double e2 {0.25};
208 double er {2.0};
209 int verb {0};
210 int nbiter = {10};
211 int pix_sz = {1};
212 string scv {"0xff0000"};
213 string isnr;
214
215
216 stringstream ssDescr;
217 ssDescr << "Computes a piecewise smooth approximation of a grey-level or color image, by optimizing the Ambrosio-Tortorelli functional (with u a 0-form and v a 1-form).";
218 ssDescr << "Usage: " << argv[0] << " -i toto.pgm\n"
219 << "Computes the Ambrosio-Tortorelli reconstruction/segmentation of an input image."
220 << "It outputs 2 or 3 images (of basename given by option --output) giving the"
221 << " reconstructed image u, and other images superposing u and the discontinuities v."
222 << endl << endl
223 << " / "
224 << endl
225 << " | a.(u-g)^2 + v^2 |grad u|^2 + le.|grad v|^2 + (l/4e).(1-v)^2 "
226 << endl
227 << " / "
228 << endl
229 << "Discretized as (u 0-form, v 1-form, A vertex-edge bdry, B edge-face bdy)" << endl
230 << "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
231 << endl
232 << "Example: ./at-u0-v1 -i ../Images/cerclesTriangle64b02.pgm -o tmp -a 0.05 -e 1 --lambda-1 0.1 --lambda-2 0.00001";
233 app.description(ssDescr.str());
234
235
236
237 app.add_option("-i,--input,1", f1, "the input image PPM filename." )
238 ->required()
239 ->check(CLI::ExistingFile);
240 app.add_option("--inpainting-mask,-m", inpainting_mask, "the input inpainting mask filename." );
241 app.add_option("--output,-o", f2, "the output image basename.");
242 auto lambdaOpt = app.add_option("--lambda,-l",l, "the parameter lambda.");
243 app.add_option("--lambda-1,-1",l1, "the initial parameter lambda (l1).");
244 app.add_option("--lambda-2,-2",l2, "the final parameter lambda (l2)." );
245 app.add_option("--lambda-ratio,-q",lr, "the division ratio for lambda from l1 to l2.");
246 app.add_option("--alpha,-a",a, "the parameter alpha.");
247 auto epsOpt = app.add_option("--epsilon,-e", "the initial and final parameter epsilon of AT functional at the same time.");
248
249 app.add_option("--epsilon-1",e1, "the initial parameter epsilon.");
250 app.add_option("--epsilon-2",e2, "the final parameter epsilon.");
251 app.add_option("--epsilon-r",er, "sets the ratio between two consecutive epsilon values of AT functional.");
252
253 app.add_option("--nbiter,-n",nbiter, "the maximum number of iterations." );
254 auto snrOpt = app.add_option("--image-snr", isnr, "the input image without deterioration if you wish to compute the SNR.");
255 app.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).");
256 app.add_option("--color-v,-c",scv, "the color chosen for displaying the singularities v (e.g. red is 0xff0000)." );
257 app.add_option("--verbose,-v", verb, "the verbose level (0: silent, 1: less silent, etc)." );
258
259 app.get_formatter()->column_width(40);
260 CLI11_PARSE(app, argc, argv);
261 // END parse command line using CLI ----------------------------------------------
262
263
264
265 Color color_v( (unsigned int) std::stoul( scv, nullptr, 16 ), 255 );
266 if ( lambdaOpt->count()) l1 = l2 = l;
267 if ( l2 > l1 ) l2 = l1;
268 if ( lr <= 1.0 ) lr = sqrt(2);
269 if ( epsOpt->count() > 0 ){
270 e1 = e2 = epsilon;
271
272 }
273 bool snr = snrOpt->count() > 0;
274
275
276 bool color_image = f1.size() > 4 && f1.compare( f1.size() - 4, 4, ".ppm" ) == 0;
277 bool grey_image = f1.size() > 4 && f1.compare( f1.size() - 4, 4, ".pgm" ) == 0;
278 if ( ! color_image && ! grey_image )
279 {
280 trace.error() << "Input image file must be either a PGM (grey-level) or a PPM (color) image with these extensions."
281 << endl;
282 return 2;
283 }
284
285 KSpace K;
286 ATu0v1< KSpace > AT( verb );
287 Domain domain;
288
289 typedef ATu0v1<KSpace>::Calculus Calculus;
290 typedef ImageContainerBySTLVector<Domain, Color> ColorImage;
291 typedef ImageContainerBySTLVector<Domain, unsigned char> GreyLevelImage;
292 //---------------------------------------------------------------------------
293 if ( color_image )
294 {
295 trace.beginBlock("Reading PPM image");
296 ColorImage image = PPMReader<ColorImage>::importPPM( f1 );
297 trace.endBlock();
298 trace.beginBlock("Building AT");
299 domain = image.domain();
300 K.init( domain.lowerBound(), domain.upperBound() - Point::diagonal( 1 ), true );
301 AT.init( K );
302 AT.addInput( image, [] ( Color c ) -> double { return ((double) c.red()) / 255.0; } );
303 AT.addInput( image, [] ( Color c ) -> double { return ((double) c.green()) / 255.0; } );
304 AT.addInput( image, [] ( Color c ) -> double { return ((double) c.blue()) / 255.0; } );
305 trace.endBlock();
306 }
307 else if ( grey_image )
308 {
309 trace.beginBlock("Reading PGM image");
310 GreyLevelImage image = PGMReader<GreyLevelImage>::importPGM( f1 );
311 trace.endBlock();
312 trace.beginBlock("Building AT");
313 domain = image.domain();
314 K.init( domain.lowerBound(), domain.upperBound() - Point::diagonal( 1 ), true );
315 AT.init( K );
316 AT.addInput( image, [] (unsigned char c ) { return ((double) c) / 255.0; } );
317 trace.endBlock();
318 }
319
320 //---------------------------------------------------------------------------
321 if ( snr && color_image )
322 {
323 trace.beginBlock("Reading ideal PPM image");
324 ColorImage image = PPMReader<ColorImage>::importPPM( isnr );
325 trace.endBlock();
326 AT.addInput( image, [] ( Color c ) -> double { return ((double) c.red()) / 255.0; }, true );
327 AT.addInput( image, [] ( Color c ) -> double { return ((double) c.green()) / 255.0; }, true );
328 AT.addInput( image, [] ( Color c ) -> double { return ((double) c.blue()) / 255.0; }, true );
329 }
330 else if ( snr && grey_image )
331 {
332 trace.beginBlock("Reading ideal PGM image");
333 GreyLevelImage image = PGMReader<GreyLevelImage>::importPGM( isnr );
334 trace.endBlock();
335 AT.addInput( image, [] (unsigned char c ) { return ((double) c) / 255.0; }, true );
336 }
337
338 //---------------------------------------------------------------------------
339 // Prepare zoomed output domain
340 Domain out_domain( pix_sz * domain.lowerBound(),
341 pix_sz * domain.upperBound() + Point::diagonal( pix_sz - 1) );
342 //---------------------------------------------------------------------------
343 AT.setUFromInput();
344 double g_snr = snr ? AT.computeSNR() : 0.0;
345
346 if ( inpainting_mask.size() > 0 )
347 {
348 string fm = inpainting_mask;
349 trace.beginBlock("Reading inpainting mask");
350 GreyLevelImage mask = GenericReader<GreyLevelImage>::import( fm );
351 trace.endBlock();
352 Calculus::PrimalForm0 m( AT.calculus );
353 for ( Calculus::Index index = 0; index < m.myContainer.rows(); index++)
354 {
355 auto cell = m.getSCell( index );
356 double col = ((double) mask( K.sCoords( cell ) )) / 255.0;
357 m.myContainer( index ) = col > 0.0 ? 1.0 : 0.0;
358 }
359 AT.setAlpha( a, m );
360 if ( grey_image )
361 {
362 ostringstream ossGM;
363 ossGM << boost::format("%s-g-mask.pgm") %f2;
364 GreyLevelImage image_mg( domain );
365 Calculus::DualForm2 mg = AT.primal_h0 * functions::dec::diagonal( m ) * AT.getG( 0 );
367 ( AT.calculus, mg, image_mg, 0.0, 1.0, 1 );
368 PGMWriter<GreyLevelImage>::exportPGM( ossGM.str(), image_mg );
369 }
370 else if ( color_image )
371 {
372 ostringstream ossGM;
373 ossGM << boost::format("%s-g-mask.ppm") %f2;
374 ColorImage image_mg( domain );
375 Calculus::DualForm2 mg0 = AT.primal_h0 * functions::dec::diagonal( m ) * AT.getG( 0 );
376 Calculus::DualForm2 mg1 = AT.primal_h0 * functions::dec::diagonal( m ) * AT.getG( 1 );
377 Calculus::DualForm2 mg2 = AT.primal_h0 * functions::dec::diagonal( m ) * AT.getG( 2 );
379 ( AT.calculus, mg0, mg1, mg2, image_mg, 0.0, 1.0, 1 );
380 PPMWriter<ColorImage, functors::Identity >::exportPPM( ossGM.str(), image_mg );
381 }
382 }
383 else
384 AT.setAlpha( a );
385
386 trace.info() << AT << std::endl;
387 double n_v = 0.0;
388 double eps = 0.0;
389 while ( l1 >= l2 )
390 {
391 trace.info() << "************ lambda = " << l1 << " **************" << endl;
392 AT.setLambda( l1 );
393 for ( eps = e1; eps >= e2; eps /= er )
394 {
395 trace.info() << " ======= epsilon = " << eps << " ========" << endl;
396 AT.setEpsilon( eps );
397 int n = 0;
398 do {
399 trace.progressBar( n, nbiter );
400 AT.solveU();
401 AT.solveV();
402 AT.checkV();
403 n_v = AT.computeVariation();
404 } while ( ( n_v > 0.0001 ) && ( ++n < nbiter ) );
405 trace.progressBar( n, nbiter );
406 trace.info() << "[#### last variation = " << n_v << " " << endl;
407 }
408 if ( grey_image )
409 {
410 if ( verb > 0 ) trace.beginBlock("Writing u[0] as PGM image");
411 ostringstream ossU, ossV, ossW;
412 ossU << boost::format("%s-a%.5f-l%.7f-u.pgm") % f2 % a % l1;
413 ossV << boost::format("%s-a%.5f-l%.7f-u-v.pgm") % f2 % a % l1;
414 ossW << boost::format("%s-a%.5f-l%.7f-u-v.ppm") % f2 % a % l1;
415 Calculus::DualForm2 u = AT.primal_h0 * AT.getU( 0 );
416 Calculus::DualForm1 v = AT.primal_h1 * AT.getV();
417 // Restored image
418 GreyLevelImage image_u( domain );
420 ( AT.calculus, u, image_u, 0.0, 1.0, 1 );
421 PGMWriter<GreyLevelImage>::exportPGM( ossU.str(), image_u );
422 // Zoomed restored image with discontinuities (in black).
423 GreyLevelImage image_uv( out_domain );
425 ( AT.calculus, u, image_uv, 0.0, 1.0, pix_sz );
427 ( AT.calculus, v, image_uv, 0.0, 1.0, pix_sz );
428 PGMWriter<GreyLevelImage>::exportPGM( ossV.str(), image_uv );
429 // Zoomed restored image with discontinuities (in specified color).
430 ColorImage cimage( out_domain );
432 ( AT.calculus, u, u, u, cimage, 0.0, 1.0, pix_sz );
434 ( AT.calculus, v, cimage, color_v, 0.0, 1.0, pix_sz );
435 PPMWriter<ColorImage, functors::Identity >::exportPPM( ossW.str(), cimage );
436 if ( verb > 0 ) trace.endBlock();
437 }
438 else if ( color_image )
439 {
440 if ( verb > 0 ) trace.beginBlock("Writing u[0,1,2] as PGM image");
441 ostringstream ossU, ossV;
442 ossU << boost::format("%s-a%.5f-l%.7f-u.ppm") % f2 % a % l1;
443 ossV << boost::format("%s-a%.5f-l%.7f-u-v.ppm") % f2 % a % l1;
444 Calculus::DualForm2 u0 = AT.primal_h0 * AT.getU( 0 );
445 Calculus::DualForm2 u1 = AT.primal_h0 * AT.getU( 1 );
446 Calculus::DualForm2 u2 = AT.primal_h0 * AT.getU( 2 );
447 Calculus::DualForm1 v = AT.primal_h1 * AT.getV();
448 // Restored image
449 ColorImage image_u( domain );
451 ( AT.calculus, u0, u1, u2, image_u, 0.0, 1.0, 1 );
452 PPMWriter<ColorImage, functors::Identity >::exportPPM( ossU.str(), image_u );
453 ColorImage image_uv( out_domain );
455 ( AT.calculus, u0, u1, u2, image_uv, 0.0, 1.0, pix_sz );
457 ( AT.calculus, v, image_uv, color_v, 0.0, 1.0, pix_sz );
458 PPMWriter<ColorImage, functors::Identity >::exportPPM( ossV.str(), image_uv );
459 if ( verb > 0 ) trace.endBlock();
460 }
461 // Compute SNR if possible
462 if ( snr )
463 {
464 double u_snr = AT.computeSNR();
465 trace.info() << "- SNR of u = " << u_snr << " SNR of g = " << g_snr << endl;
466 }
467 l1 /= lr;
468 }
469 return 0;
470}
void dualForm1ToGreyLevelImage(const Calculus &calculus, const typename Calculus::DualForm1 &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 dualForm1ToRGBColorImage(const Calculus &calculus, const typename Calculus::DualForm1 &v, Image &image, Color color, 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)
DGtal::LinearOperator< Calculus, dim, duality, dim, duality > diagonal(const DGtal::KForm< Calculus, dim, duality > &kform)
Definition ATu0v1.h:57
Aim: This class solves Ambrosio-Tortorelli functional in a plane for u a (vector of) 0-form(s) and v ...
Definition ATu0v1.h:75
DiscreteExteriorCalculus< 2, 2, LinearAlgebra > Calculus