From 59fbde0754719f20e62fe7e85c890fce1056b75a Mon Sep 17 00:00:00 2001
From: Wolfgang Ludwig <wolfgang.ludwig@esrf.fr>
Date: Tue, 5 Jun 2018 18:36:32 +0200
Subject: [PATCH] Fix issue with offset between Absorption and Grain
 reconstructions Signed-off-by: Wolfgang Ludwig <wolfgang.ludwig@esrf.fr>

---
 .../gtAstraPrepareAbsorptionStack.m           |  9 ++++---
 5_reconstruction/gtAstra_FBP2D.m              | 22 ++++++++--------
 zUtil_ForwardSim/gtFwdSimBuildProjGeometry.m  | 16 +++++++++---
 zUtil_Geo/gtGeoDetDefaultParameters.m         | 12 +++++----
 zUtil_Geo/gtGeoProjForReconstruction.m        | 25 +++++++++++--------
 zUtil_Geo/gtGeoRecDefaultParameters.m         | 11 +++++---
 zUtil_Parameters/build_list_v2.m              |  3 ++-
 7 files changed, 60 insertions(+), 38 deletions(-)

diff --git a/5_reconstruction/gtAstraPrepareAbsorptionStack.m b/5_reconstruction/gtAstraPrepareAbsorptionStack.m
index a60e0160..1a1b2464 100644
--- a/5_reconstruction/gtAstraPrepareAbsorptionStack.m
+++ b/5_reconstruction/gtAstraPrepareAbsorptionStack.m
@@ -73,11 +73,13 @@ imgs_nums = 0 : rec_abs.interval : totproj-1;
 Omega_rad = angle_rad(imgs_nums + 1);
 Omega_deg = Omega_rad * 180 / pi;
 
+%bb = [acq.bb(1), detgeo.detrefv - acq.bb(4) / 2 + 0.5, acq.bb(3), acq.bb(4)];
+
 bbpos = repmat(acq.bb, [numel(Omega_deg), 1]);
 % bbpos = [gtGeoGrainCenterSam2Det(samgeo.orig, 0, parameters) - (acq.bb(3:4) - 1) / 2, acq.bb(3:4)];
 % bbpos = repmat(bbpos, [numel(Omega_deg), 1]);
 [~, gr_shift] = gtMatchGetSampleShifts(parameters, Omega_deg);
-
+extra_detector_offset = acq.rotu - (detgeo.detrefu - detgeo.detrefpos(2) / detgeo.pixelsizeu);  %sub-pixel shift for 2D-FBP algorithm
 geom = gtGeoProjForReconstruction([], Omega_deg', [], bbpos, [], ...
     detgeo, labgeo, samgeo, recgeo, 'ASTRA_absorption', gr_shift);
 if (~isempty(conf.rot_angles))
@@ -117,7 +119,7 @@ for jj = 1:numel(imgs_nums)
 
     fprintf(repmat('\b', 1, num_chars))
 end
-stack = -log(max(0.01, stack));
+stack = -log(max(0.001, stack));
 
 fprintf('Done in %f seconds.\n', toc(c));
 
@@ -132,7 +134,7 @@ fprintf('...done!\n')
 % Dealing with the padding
 new_stack_size = stack_size + [2, 0, 2] * rec_abs.padding;
 new_stack_shifts = [1, 0, 1] * rec_abs.padding;
-stack(stack < 0) = 0;
+%stack(stack < 0) = 0;
 new_stack = gtPlaceSubVolume(zeros(new_stack_size, 'single'), stack, new_stack_shifts);
 new_stack(1:rec_abs.padding, :) = new_stack((rec_abs.padding + 1) * ones(rec_abs.padding, 1), :);
 new_stack((end - rec_abs.padding + 1):end, :) = new_stack((end - rec_abs.padding) * ones(rec_abs.padding, 1), :);
@@ -142,6 +144,7 @@ proj = struct( ...
     'psf', rec_abs.psf, ...
     'rot_angles', conf.rot_angles, ... % Additional rotation
     'rot_axes', conf.rot_axes, ...   % Additional rotation
+    'extra_detector_offset', extra_detector_offset, ... % Correction for sub-pixel shift of rotation axis in 2D-FBP algorithm
     'stack', new_stack, ...
     'Omega', Omega_rad, ...
     'geom', geom, ...
diff --git a/5_reconstruction/gtAstra_FBP2D.m b/5_reconstruction/gtAstra_FBP2D.m
index bb0aab30..cb5a930a 100644
--- a/5_reconstruction/gtAstra_FBP2D.m
+++ b/5_reconstruction/gtAstra_FBP2D.m
@@ -34,10 +34,7 @@ function [absvol, res_norm] = gtAstra_FBP2D(parameters, proj, extra_detector_off
     hsize = size(proj.stack, 1);  % number of pixels in projection image
     nproj = size(proj.stack, 2);  % number of projections
 
-    hsize_zp = ceil(hsize * sqrt(2));   % zeropad projection images
-    offset = ceil((hsize_zp - hsize) / 2);
-
-    sino = zeros(nproj, hsize_zp);
+    sino = zeros(nproj, hsize);
 
     xvol = hsize;
     yvol = hsize;
@@ -46,13 +43,13 @@ function [absvol, res_norm] = gtAstra_FBP2D(parameters, proj, extra_detector_off
     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     % projection geometry (type, #rows, #columns, vectors)
     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-    proj_geom = astra_create_proj_geom('parallel', 1.0, hsize_zp, proj.Omega);
+    proj_geom = astra_create_proj_geom('parallel', 1.0, hsize, proj.Omega);
     omegas = abs(proj.Omega) / pi * 180;
     [shift_lab, shift_sam] = gtMatchGetSampleShifts(parameters, omegas);
     proj_geom.option.ExtraDetectorOffset = shift_lab(:,2)' ./ parameters.detgeo.pixelsizeu;
-    if exist('extra_detector_offset', 'var')
+    if isfield(proj, 'extra_detector_offset')
         disp('shifting projections');
-        proj_geom.option.ExtraDetectorOffset = proj_geom.option.ExtraDetectorOffset + ones(1, nproj) * extra_detector_offset;
+        proj_geom.option.ExtraDetectorOffset = proj_geom.option.ExtraDetectorOffset + ones(1, nproj) * proj.extra_detector_offset;
     end
 
     vol_geom = astra_create_vol_geom(hsize, hsize);
@@ -77,11 +74,12 @@ function [absvol, res_norm] = gtAstra_FBP2D(parameters, proj, extra_detector_off
         % Not sure if we really need to re-create the algorithm each time..
         alg_id = astra_mex_algorithm('create', cfg);
 
-        % 'zeropadding' projections with the value
-        sino = gtPlaceSubImage(squeeze(proj_data(:, :, ii))', sino, offset, 1);
-
-        sino(:, 1:offset-1) = repmat(sino(:, offset), 1, offset-1);
-        sino(:, offset+hsize:end) = repmat(sino(:, offset+hsize-1), 1, hsize_zp-offset-hsize+1);
+        % 'zeropadding' projections is already dealt with in
+        % gtAstraPrepareAbsorptionStack
+        %sino = gtPlaceSubImage(squeeze(proj_data(:, :, ii))', sino, offset, 1);
+        sino = proj_data(:, :, ii)';
+        %sino(:, 1:offset-1) = repmat(sino(:, offset), 1, offset-1);
+        %sino(:, offset+hsize:end) = repmat(sino(:, offset+hsize-1), 1, hsize_zp-offset-hsize+1);
 
         astra_mex_data2d('set', sinogram_id, sino)
 
diff --git a/zUtil_ForwardSim/gtFwdSimBuildProjGeometry.m b/zUtil_ForwardSim/gtFwdSimBuildProjGeometry.m
index f6011046..d529be66 100644
--- a/zUtil_ForwardSim/gtFwdSimBuildProjGeometry.m
+++ b/zUtil_ForwardSim/gtFwdSimBuildProjGeometry.m
@@ -35,7 +35,17 @@ function [proj, verts] = gtFwdSimBuildProjGeometry(diff_beam_dirs, gr_center, om
     % Calculate the projection geometry for the spots:
     %  - Vector used for ROI grain reconstruction in ASTRA (grain shifted
     %    to center of roi volume)
-    [gr_shift_lab, gr_shift_sam] = gtMatchGetSampleShifts(parameters, omegas);
+    if isfield(parameters.acq, 'correct_sample_shifts')
+        if parameters.acq.correct_sample_shifts
+            [gr_shift_lab, gr_shift_sam] = gtMatchGetSampleShifts(parameters, omegas);
+        else
+            gr_shift_lab = [];
+            gr_shift_sam = [];
+        end
+    else
+        gr_shift_lab = [];
+        gr_shift_sam = [];
+    end
     proj_geom = gtGeoProjForReconstruction(diff_beam_dirs, ...
         omegas, gr_center, bbpos_det_grain, [], ...
         detgeo, labgeo, samgeo, recgeo, 'ASTRA_grain', gr_shift_sam);
@@ -65,12 +75,12 @@ function [proj, verts] = gtFwdSimBuildProjGeometry(diff_beam_dirs, gr_center, om
     proj.centerpix = gtGeoSam2Sam(gr_center, samgeo, recgeo, true);
 
     use_polyhedron = ~cloning & (numel(find(selected)) >= 3);
-    if (use_polyhedron)
+    if 0 %(use_polyhedron)
         % This should behave better with vertical detector
         % (if no bug is introduced with it :D)
         verts = gtFwdSimComputeCircumscribingPolyhedron(...
             gr_center, diff_beam_dirs(selected, :), omegas(selected), ...
-            bb(selected, :), parameters, det_ind);
+            bb(selected, :), gr_shift_lab(selected, :), parameters, det_ind, 'convhull', false);
 
         vol_size = 2 * max(abs(max(verts, [], 1)), abs(min(verts, [], 1)));
     else
diff --git a/zUtil_Geo/gtGeoDetDefaultParameters.m b/zUtil_Geo/gtGeoDetDefaultParameters.m
index 205fe856..ed267cb3 100644
--- a/zUtil_Geo/gtGeoDetDefaultParameters.m
+++ b/zUtil_Geo/gtGeoDetDefaultParameters.m
@@ -89,10 +89,12 @@ function d_yz = sfDetrefpos(acq_bb, par_labgeo)
 % account for an offset of the sample bounding box position:
 %   Y lateral: It assumes that the projection of the rotation axis is at the
 %   center of the sample bounding box.
-%   Z vertical: set in a way that the vertical center of the sample bounding
-%   box will have coordinate Z=0.
-% It assumes rotpos = [0 0 0] and view into the beam.
+%   Z vertical: By definition we set the detector center as 0 position
+%
+%   NB: Obsolete definition:  Vertical center of the sample bounding (beam center) corresponds to Z=0.
+%   It assumes rotpos = [0 0 0] and view into the beam.
 
-    d_yz(1) = -((acq_bb(1) + acq_bb(3)/2) - par_labgeo.detrefu) * par_labgeo.pixelsizeu;
-    d_yz(2) =  ((acq_bb(2) + acq_bb(4)/2) - par_labgeo.detrefv) * par_labgeo.pixelsizev;
+    d_yz(1) = -((acq_bb(1) + acq_bb(3)/2) - par_labgeo.detrefu - 0.5) * par_labgeo.pixelsizeu;
+    %d_yz(2) =  ((acq_bb(2) + acq_bb(4)/2) - par_labgeo.detrefv - 0.5) * par_labgeo.pixelsizev;
+    d_yz(2) = 0;  % By definition
 end
\ No newline at end of file
diff --git a/zUtil_Geo/gtGeoProjForReconstruction.m b/zUtil_Geo/gtGeoProjForReconstruction.m
index c5020451..26e290c3 100644
--- a/zUtil_Geo/gtGeoProjForReconstruction.m
+++ b/zUtil_Geo/gtGeoProjForReconstruction.m
@@ -105,18 +105,21 @@ function proj_geom = gtGeoProjForReconstruction(projvec_sam, omega, vol_center,.
     end
     projvec_rec = gtMathsNormalizeVectorsList(projvec_rec);
 
-    if (strcmpi(rectype, 'ASTRA_grain') || strcmpi(rectype, 'ASTRA_absorption'))
-        % Volume center in REC reference
-        if (strcmpi(rectype, 'ASTRA_absorption'))
-            vol_center_rec = recgeo.orig;
-        else
+
+    switch rectype
+        case 'ASTRA_absorption'
+            % Volume center in REC reference
+            samorig_rec = gtGeoSam2Sam(samgeo.orig, samgeo, recgeo, false, false);
+            proj_shift  = samorig_rec(ones_omega, :) - vol_center_shift_rec;
+            bbcent_rec  = bbcent_rec + proj_shift;
+
+        case 'ASTRA_grain'
             vol_center_rec = gtGeoSam2Sam(vol_center, samgeo, recgeo, false, false);
-        end
-        vol_center_rec = vol_center_rec(ones_omega, :) + vol_center_shift_rec;
-        bbcent_rec = bbcent_rec - vol_center_rec;
-    else
-        % case {'ASTRA_full'}
-        bbcent_rec = bbcent_rec - vol_center_shift_rec;
+            proj_shift     = -vol_center_rec(ones_omega, :) - vol_center_shift_rec;
+            bbcent_rec     = bbcent_rec + proj_shift;
+
+        case 'ASTRA_full'
+            bbcent_rec = bbcent_rec - vol_center_shift_rec;
     end
 
     proj_geom = [projvec_rec, bbcent_rec, detdiru_rec, detdirv_rec];
diff --git a/zUtil_Geo/gtGeoRecDefaultParameters.m b/zUtil_Geo/gtGeoRecDefaultParameters.m
index 7bfbde78..045cdd2a 100644
--- a/zUtil_Geo/gtGeoRecDefaultParameters.m
+++ b/zUtil_Geo/gtGeoRecDefaultParameters.m
@@ -1,12 +1,13 @@
-function par_recgeo = gtGeoRecDefaultParameters(detgeo, samgeo)
+function par_recgeo = gtGeoRecDefaultParameters(detgeo, samgeo, acq)
 % GTGEOSAMDEFAULTPARAMETERS  Stores and returns the default Sample geomtery
 %                            parameters
 %
-%     par_recgeo = gtGeoRecDefaultParameters([detgeo [, samgeo]])
+%     par_recgeo = gtGeoRecDefaultParameters([detgeo [, samgeo, acq]])
 %     ---------------------------------------
 %     The default Reconstruction reference is set as the Lab but rotating with the
 %     sample. Voxel size is determined from detector pixelsize.
-%
+%     If acq is provided, the vertical position is offset to match the illuminated region defined by acq.bb
+%     otherwise it is set to 0 (center of detector)
 
     if (exist('samgeo', 'var') && ~isempty(samgeo))
         par_recgeo.orig = samgeo.orig;
@@ -26,5 +27,9 @@ function par_recgeo = gtGeoRecDefaultParameters(detgeo, samgeo)
         det_pixel_size = 1e-3;
     end
 
+    if (exist('acq', 'var') && ~isempty(acq))
+        par_recgeo.orig(3) = -(acq.bb(2) + acq.bb(4) / 2 - detgeo.detrefv - 0.5) * detgeo.pixelsizev;
+    end
+
     par_recgeo.voxsize = det_pixel_size([1 1 1]);
 end
diff --git a/zUtil_Parameters/build_list_v2.m b/zUtil_Parameters/build_list_v2.m
index 24cd2370..3649440c 100644
--- a/zUtil_Parameters/build_list_v2.m
+++ b/zUtil_Parameters/build_list_v2.m
@@ -610,7 +610,8 @@ list.fsim(end+1, :) = {'sel_int_stdev', ...
     'Selects spots for reconsruction that are in the top 4 sigma of the intensity rankings', 'double', 1};
 list.fsim(end+1, :) = {'sel_avint_stdev', ...
     'Selects spots for reconsruction that are in the top 3.5 sigma of the average intensity per pixel rankings', 'double', 1};
-
+list.fsim(end+1, :) = {'mode', ...
+    'Mode of operation {''indexter''}/''farfield''/''cloning''', 'char', 1};
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 % reconstruction
-- 
GitLab