From 1fbcb871342f491068056742d1ddf29ce2a70d45 Mon Sep 17 00:00:00 2001 From: Nicola Vigano <nicola.vigano@esrf.fr> Date: Mon, 9 Jan 2017 17:34:17 +0100 Subject: [PATCH] Improved 3DTV absorption volume reconstruction, with KL data divergence term, and l12 norm in the TV term Signed-off-by: Nicola Vigano <nicola.vigano@esrf.fr> --- 5_reconstruction/gtAstra3DTV.m | 39 ++++++++++++++----- 5_reconstruction/gtAstraAbsorptionVolume.m | 34 ++++++++++------ .../gtAstraPrepareAbsorptionStack.m | 2 +- .../gtRecAbsorptionDefaultParameters.m | 24 ++++++++++++ 5_reconstruction/gtRecDefaultParameters.m | 4 +- 5 files changed, 78 insertions(+), 25 deletions(-) create mode 100644 5_reconstruction/gtRecAbsorptionDefaultParameters.m diff --git a/5_reconstruction/gtAstra3DTV.m b/5_reconstruction/gtAstra3DTV.m index 6d92b6ed..19916ddf 100644 --- a/5_reconstruction/gtAstra3DTV.m +++ b/5_reconstruction/gtAstra3DTV.m @@ -33,7 +33,12 @@ function volume = gtAstra3DTV(proj, varargin) % Version 002 26-09-2012 by LNervo % Formatting, cleaning, commenting - conf = struct('verbose', false, 'is_cone', false, 'epsilon', 1e-4); + conf = struct(... + 'verbose', false, ... + 'is_cone', false, ... + 'detector_norm', 'l2', ... + 'lambda', 1e-2, ... + 'epsilon', 1e-4); conf = parse_pv_pairs(conf, varargin); @@ -120,8 +125,13 @@ function volume = gtAstra3DTV(proj, varargin) stats.add_task('primal_update', 'Update of Primal') stats.add_task('grad_comp', 'Computing the Gradient') stats.add_task('div_comp', 'Computing the Divergence') + res_initial = gtMathsNorm_l2(proj.stack); residuals = [1; zeros(proj.num_iter, 1)]; + if (strcmpi(conf.detector_norm, 'kl')) + data_term = 4 .* sigma .* proj.stack; + end + fprintf('\b\b: ') for ii = 1:proj.num_iter char_num = fprintf('%02d/%02d (res: %f)', ii-1, proj.num_iter, residuals(ii)); @@ -129,14 +139,24 @@ function volume = gtAstra3DTV(proj, varargin) stats.tic('fp') tomo_fp_tmp = fp(volume_enh); stats.toc('fp') - residuals(ii+1) = sqrt(gtMathsNorm_l2(tomo_fp_tmp - proj.stack) / res_initial); + residuals(ii+1) = gtMathsNorm_l2(tomo_fp_tmp - proj.stack) / res_initial; stats.tic('dual_det_update') - if (conf.epsilon >= 0) - temp_dual_det = dual_det + sigma .* (tomo_fp_tmp - proj.stack); - temp_dual_denom = temp_dual_det + (temp_dual_det == 0); - dual_det = temp_dual_det .* max(1 - sigma .* conf.epsilon ./ temp_dual_denom, 0); - else - dual_det = (dual_det + sigma .* (tomo_fp_tmp - proj.stack)) .* sigma_1; + switch (lower(conf.detector_norm)) + case 'l2' + if (conf.epsilon >= 0) + temp_dual_det = dual_det + sigma .* (tomo_fp_tmp - proj.stack); + temp_dual_denom = temp_dual_det + (temp_dual_det == 0); + dual_det = temp_dual_det .* max(1 - sigma .* conf.epsilon ./ temp_dual_denom, 0); + else + dual_det = (dual_det + sigma .* (tomo_fp_tmp - proj.stack)) .* sigma_1; + end + case 'kl' + temp_dual = dual_det + sigma .* tomo_fp_tmp; + dual_det = (1 + temp_dual - sqrt((temp_dual - 1) .^ 2 + data_term)) ./ 2; + otherwise + error([mfilename ':wrong_argument'], ... + 'Detector divergence term: "%s" not recognized', ... + conf.detector_norm) end stats.toc('dual_det_update') @@ -145,7 +165,8 @@ function volume = gtAstra3DTV(proj, varargin) stats.toc('grad_comp') stats.tic('dual_grad_update') temp_dual_tv = dual_tv + d; - dual_tv = temp_dual_tv ./ max(1, abs(temp_dual_tv)); + norm_dual_tv = max(1, sqrt(sum(temp_dual_tv .^ 2, 4))); + dual_tv = temp_dual_tv ./ norm_dual_tv(:, :, :, [1 1 1]); stats.toc('dual_grad_update') stats.tic('bp') diff --git a/5_reconstruction/gtAstraAbsorptionVolume.m b/5_reconstruction/gtAstraAbsorptionVolume.m index 07b10e8c..d61bc633 100644 --- a/5_reconstruction/gtAstraAbsorptionVolume.m +++ b/5_reconstruction/gtAstraAbsorptionVolume.m @@ -1,4 +1,4 @@ -function abs = gtAstraAbsorptionVolume(parameters) +function [abs, proj] = gtAstraAbsorptionVolume(parameters, proj) % GTASTRAABSORPTIONVOLUME reconstruct absorption volume using 3D SIRT or 2D FBP % abs = gtAstraAbsorptionVolume(parameters) % ----------------------------------------- @@ -13,24 +13,29 @@ function abs = gtAstraAbsorptionVolume(parameters) if ~exist('parameters','var') || isempty(parameters) - load('parameters.mat'); + parameters = gtLoadParameters(); end projname = fullfile(parameters.acq.dir, 'absorption_stack.mat'); - if (exist(projname, 'file')) - check = inputwdefault('Absorption stack already exists - do you want to (re)-create it? [y/n]', 'n'); + if (~exist('proj', 'var') || isempty(proj)) + if (exist(projname, 'file')) + check = inputwdefault('Absorption stack already exists - do you want to (re)-create it? [y/n]', 'n'); - if strcmpi(check, 'n') - fprintf('Loading absorption stack..') - c = tic(); - proj = load(projname); - fprintf('\b\b: Done. (%f seconds)\n', toc(c)) + if strcmpi(check, 'n') + fprintf('Loading absorption stack..') + c = tic(); + proj = load(projname); + fprintf('\b\b: Done. (%f seconds)\n', toc(c)) + else + proj = gtAstraPrepareAbsorptionStack(parameters); + end else + fprintf('Absorption stack doesn''t exist, yet. Building it..') + c = tic(); proj = gtAstraPrepareAbsorptionStack(parameters); + fprintf('\b\b: Done in %g seconds.\n', toc(c)); end - else - proj = gtAstraPrepareAbsorptionStack(parameters); end if (isfield(parameters.rec, 'absorption')) @@ -50,7 +55,12 @@ function abs = gtAstraAbsorptionVolume(parameters) case {'3DART', 'SIRT'} abs = gtAstra3D(proj, 'is_cone', is_cone); case {'3DTV'} - abs = gtAstra3DTV(proj, 'is_cone', is_cone); + if (~isfield(rec_abs, 'options') || isempty(rec_abs.options)) + rec_abs_options = {}; + else + rec_abs_options = [fieldnames(rec_abs.options), struct2cell(rec_abs.options)]'; + end + abs = gtAstra3DTV(proj, 'is_cone', is_cone, rec_abs_options{:}); case '2DFBP' abs = gtAstra_FBP2D(parameters, proj); otherwise diff --git a/5_reconstruction/gtAstraPrepareAbsorptionStack.m b/5_reconstruction/gtAstraPrepareAbsorptionStack.m index be9a1003..102bbfa4 100644 --- a/5_reconstruction/gtAstraPrepareAbsorptionStack.m +++ b/5_reconstruction/gtAstraPrepareAbsorptionStack.m @@ -62,7 +62,7 @@ imgs_nums = 0 : rec_abs.interval : totproj-1; Omega_rad = angle_rad(imgs_nums + 1); Omega_deg = Omega_rad * 180 / pi; -bbpos = repmat(acq.bb, numel(Omega_deg), []); +bbpos = repmat(acq.bb, [numel(Omega_deg), 1]); geom = gtGeoProjForReconstruction([], Omega_deg', [], bbpos, [], ... detgeo, labgeo, samgeo, recgeo, 'ASTRA_absorption'); geom(:, 10:12) = round(geom(:, 10:12)); diff --git a/5_reconstruction/gtRecAbsorptionDefaultParameters.m b/5_reconstruction/gtRecAbsorptionDefaultParameters.m new file mode 100644 index 00000000..3ab191b1 --- /dev/null +++ b/5_reconstruction/gtRecAbsorptionDefaultParameters.m @@ -0,0 +1,24 @@ +function par_rec = gtRecAbsorptionDefaultParameters(algo) +% FUNCTION par_rec = gtRecAbsorptionDefaultParameters(algo) +% Arguments can be (default in {}): +% - algo : {'SIRT'} | '2DFBP' | '3DTV' +% + + switch (upper(algo)) + case {'SIRT', '2DFBP'} + par_rec = struct(... + 'algorithm', upper(algo), 'num_iter', 30, ... + 'interval', 10, 'padding', 5, 'options', []); + + case '3DTV' + par_3DTV_rec_opts = struct( ... + 'verbose', false, ... + 'detector_norm', 'l2', ... + 'lambda', 1e-2, ... + 'epsilon', 1e-4 ... + ); + par_rec = struct(... + 'algorithm', upper(algo), 'num_iter', 30, 'interval', 10, ... + 'padding', 5, 'options', par_3DTV_rec_opts); + end +end diff --git a/5_reconstruction/gtRecDefaultParameters.m b/5_reconstruction/gtRecDefaultParameters.m index 17eecbe6..433429fb 100644 --- a/5_reconstruction/gtRecDefaultParameters.m +++ b/5_reconstruction/gtRecDefaultParameters.m @@ -11,9 +11,7 @@ function par_rec = gtRecDefaultParameters(varargin) conf = parse_pv_pairs(conf, varargin); % Absorption volume options - par_rec.absorption = struct( ... - 'algorithm', conf.absorption_algo, 'num_iter', 30, ... - 'interval', 10, 'padding', 5, 'options', [] ); + par_rec.absorption = gtRecAbsorptionDefaultParameters(conf.absorption_algo); % Grain volumes options par_rec.grains = gtRecGrainsDefaultParameters(conf.grains_algo); -- GitLab