From c110bd615af1b4c90afbb44793881a893c414ee0 Mon Sep 17 00:00:00 2001
From: Nicola Vigano <nicola.vigano@esrf.fr>
Date: Sat, 3 Mar 2018 17:59:18 +0100
Subject: [PATCH] Sinos<->blobs: added support for sub-blob UV shifts

At the moment, slabbing is disabled in the shifted mode for sinos->blobs

Signed-off-by: Nicola Vigano <nicola.vigano@esrf.fr>
---
 zUtil_Cxx/include/DctData.h                   |   7 +-
 zUtil_Cxx/include/DctDataAlgorithms.h         | 195 ++++++++++++------
 zUtil_Cxx/include/DctMatlabData.h             |  25 ++-
 zUtil_Deformation/Gt6DVolumeToBlobProjector.m |  26 ++-
 4 files changed, 189 insertions(+), 64 deletions(-)

diff --git a/zUtil_Cxx/include/DctData.h b/zUtil_Cxx/include/DctData.h
index a571a50e..5cc6adae 100644
--- a/zUtil_Cxx/include/DctData.h
+++ b/zUtil_Cxx/include/DctData.h
@@ -18,7 +18,7 @@
 namespace dct {
 
   static const size_t VERSION_MAJOR = 0;
-  static const size_t VERSION_MINOR = 0;
+  static const size_t VERSION_MINOR = 1;
 
   // This class is intentionally not memory-safe!! Be careful with it..
   template<typename Type, class Alloc = VectorAllocator<Type> >
@@ -158,6 +158,11 @@ namespace dct {
   static const char error_wrong_dim_0[] = "Blobs and sinograms should have same length along 1st dimension";
   static const char error_wrong_dim_2[] = "Blobs and sinograms should have same length along 3rd dimension";
 
+  static const char error_incons_use_shifts[] = "Inconsistent use of shifts";
+
+  static const char error_wrong_dim_u_sh[] = "Sinograms should fit into blobs along 1st dimension";
+  static const char error_wrong_dim_v_sh[] = "Sinograms should fit into blobs along 3rd dimension";
+
   static const char error_sino_slice_out_of_hlim[] = "Requested a SINOGRAM SLICE outside the range ('sino_offsets' too large)!";
   static const char error_blob_slice_out_of_hlim[] = "Requested a BLOB SLICE outside the range ('proj_offsets' too large)!";
 
diff --git a/zUtil_Cxx/include/DctDataAlgorithms.h b/zUtil_Cxx/include/DctDataAlgorithms.h
index f9206855..f1d504e9 100644
--- a/zUtil_Cxx/include/DctDataAlgorithms.h
+++ b/zUtil_Cxx/include/DctDataAlgorithms.h
@@ -91,6 +91,7 @@ namespace dct {
   template<typename Type>
   struct DctProjectionTransformCoefficients {
     size_t num_projections;
+    bool use_shifts;
 
     std::vector<size_t> dest_slice; // <-- slice in destination sinogram / blob
 
@@ -99,33 +100,47 @@ namespace dct {
 
     std::vector<Type> coeff; // <-- saxpy coefficient
 
+    std::vector<size_t> shift_u; // <-- U shift for sinos into blobs
+    std::vector<size_t> shift_v; // <-- V shift for sinos into blobs
+
   public:
-    DctProjectionTransformCoefficients() : num_projections(0) { };
-    DctProjectionTransformCoefficients(const size_t & num_projs)
-    : num_projections(num_projs)
+    DctProjectionTransformCoefficients() : num_projections(0), use_shifts(false) { };
+    DctProjectionTransformCoefficients(const size_t & num_projs, const bool & do_use_shifts)
+    : num_projections(num_projs), use_shifts(do_use_shifts)
     {
       this->resize(num_projs);
     }
 
     void resize(const size_t & num_projs) {
       num_projections = num_projs;
+
       dest_slice.resize(num_projs);
       src_vol.resize(num_projs);
       src_slice.resize(num_projs);
       coeff.resize(num_projs);
+
+      shift_u.resize(num_projs);
+      shift_v.resize(num_projs);
     }
     void reserve(const size_t & num_projs) {
       dest_slice.reserve(num_projs);
       src_vol.reserve(num_projs);
       src_slice.reserve(num_projs);
       coeff.reserve(num_projs);
+
+      shift_u.reserve(num_projs);
+      shift_v.reserve(num_projs);
     }
-    void emplace_back(const size_t & d_slice, const size_t & s_vol, const size_t & s_slice, const Type & c)
+    void emplace_back(const size_t & d_slice, const size_t & s_vol, const size_t & s_slice, const Type & c, const size_t & d_shift_u = 0, const size_t & d_shift_v = 0)
     {
       dest_slice.emplace_back(d_slice);
       src_vol.emplace_back(s_vol);
       src_slice.emplace_back(s_slice);
       coeff.emplace_back(c);
+
+      shift_u.emplace_back(d_shift_u);
+      shift_v.emplace_back(d_shift_v);
+
       num_projections++;
     }
   };
@@ -142,12 +157,18 @@ namespace dct {
     std::vector<DctProjectionTransformCoefficients<Type> > transforms_to_sinos;
     std::vector<DctProjectionTransformCoefficients<Type> > transforms_to_blobs;
 
-    size_t line_length;
-    size_t lines_num;
+    size_t blobs_size_u;
+    size_t blobs_size_v;
+    size_t sinos_size_u;
+    size_t sinos_size_v;
+
+    bool use_shifts;
 
   public:
     DctProjectionTransform(const size_t & num_threads = std::thread::hardware_concurrency())
-    : DctDataProcess<Type, Alloc, vector_size>(num_threads), line_length(0), lines_num(0)
+    : DctDataProcess<Type, Alloc, vector_size>(num_threads)
+    , blobs_size_u(0), blobs_size_v(0), sinos_size_u(0), sinos_size_v(0)
+    , use_shifts(false)
     { }
 
     void set_blobs(const DctProjectionVector<Type, Alloc> & new_blobs);
@@ -170,8 +191,8 @@ namespace dct {
     template<typename FunctionType>
     void submit_transforms(FunctionType func, const size_t & tot_vols);
 
-    void transform_blobs2sino(const size_t & sino_num, const size_t & min_line, const size_t & max_line);
-    void transform_sinos2blob(const size_t & blob_num, const size_t & min_line, const size_t & max_line);
+    void transform_blobs2sino(const size_t & sino_num, const size_t & min_v, const size_t & max_v);
+    void transform_sinos2blob(const size_t & blob_num, const size_t & min_v, const size_t & max_v);
 
     void initialize_transforms_to_blobs();
   };
@@ -713,8 +734,8 @@ namespace dct {
     this->blobs = new_blobs;
 
     const auto & dims = blobs[0].get_dims();
-    this->line_length = dims[0];
-    this->lines_num = dims[2];
+    this->blobs_size_u = dims[0];
+    this->blobs_size_v = dims[2];
   }
 
   template<typename Type, class Alloc, const size_t vector_size>
@@ -728,8 +749,8 @@ namespace dct {
     this->sinos = new_sinos;
 
     const auto & dims = sinos[0].get_dims();
-    this->line_length = dims[0];
-    this->lines_num = dims[2];
+    this->sinos_size_u = dims[0];
+    this->sinos_size_v = dims[2];
   }
 
   template<typename Type, class Alloc, const size_t vector_size>
@@ -738,6 +759,11 @@ namespace dct {
       const std::vector<DctProjectionTransformCoefficients<Type> > & new_transforms)
   {
     this->transforms_to_sinos = new_transforms;
+    this->transforms_to_blobs.clear();
+
+    if (this->transforms_to_sinos.size() > 0) {
+      this->use_shifts = transforms_to_sinos[0].use_shifts;
+    }
   }
 
   template<typename Type, class Alloc, const size_t vector_size>
@@ -751,8 +777,8 @@ namespace dct {
     this->blobs = std::move(new_blobs);
 
     const auto & dims = blobs[0].get_dims();
-    this->line_length = dims[0];
-    this->lines_num = dims[2];
+    this->blobs_size_u = dims[0];
+    this->blobs_size_v = dims[2];
   }
 
   template<typename Type, class Alloc, const size_t vector_size>
@@ -766,8 +792,8 @@ namespace dct {
     this->sinos = std::move(new_sinos);
 
     const auto & dims = sinos[0].get_dims();
-    this->line_length = dims[0];
-    this->lines_num = dims[2];
+    this->sinos_size_u = dims[0];
+    this->sinos_size_v = dims[2];
   }
 
   template<typename Type, class Alloc, const size_t vector_size>
@@ -777,6 +803,10 @@ namespace dct {
   {
     this->transforms_to_sinos = std::move(new_transforms);
     this->transforms_to_blobs.clear();
+
+    if (this->transforms_to_sinos.size() > 0) {
+      this->use_shifts = transforms_to_sinos[0].use_shifts;
+    }
   }
 
   template<typename Type, class Alloc, const size_t vector_size>
@@ -798,6 +828,9 @@ namespace dct {
       const std::vector<size_t> & blob_offsets = transforms_to_sinos[num_sino].src_vol;
       const std::vector<size_t> & proj_offsets = transforms_to_sinos[num_sino].src_slice;
 
+      const std::vector<size_t> & shifts_u = transforms_to_sinos[num_sino].shift_u;
+      const std::vector<size_t> & shifts_v = transforms_to_sinos[num_sino].shift_v;
+
       const size_t * const dims_sino = sinos[num_sino].get_dims();
 
       for (size_t m = 0; m < transforms_to_sinos[num_sino].num_projections; m++) {
@@ -814,13 +847,34 @@ namespace dct {
         }
 
         const size_t * const dims_blob = this->blobs[b].get_dims();
-        // Sino and Blob have the same size along 1st and 3rd dimensions
-
-        if (dims_blob[0] != dims_sino[0]) {
-          THROW(WrongArgumentException, DctProjectionTransform, error_wrong_dim_0);
+        if (transforms_to_sinos[num_sino].use_shifts != this->use_shifts) {
+          THROW(WrongArgumentException, DctProjectionTransform, error_incons_use_shifts);
         }
-        if (dims_blob[2] != dims_sino[2]) {
-          THROW(WrongArgumentException, DctProjectionTransform, error_wrong_dim_2);
+
+        if (!transforms_to_sinos[num_sino].use_shifts) {
+          // Sino and Blob have the same size along 1st and 3rd dimensions in initial version
+
+          if (dims_blob[0] != dims_sino[0]) {
+            THROW(WrongArgumentException, DctProjectionTransform, error_wrong_dim_0);
+          }
+          if (dims_blob[2] != dims_sino[2]) {
+            THROW(WrongArgumentException, DctProjectionTransform, error_wrong_dim_2);
+          }
+        } else {
+          // In this case, all the sinos are supposed to fit into the blobs
+          const size_t & su = shifts_u[m];
+          const size_t & sv = shifts_v[m];
+
+          if (dims_blob[0] < (dims_sino[0] + su)) {
+            printf("Num sino: %lu, num blob: %lu, blobs UV size [%lu, %lu], sino UV size [%lu, %lu], sino shift UV [%lu, %lu]\n",
+                num_sino, b, dims_blob[0], dims_blob[2], dims_sino[0], dims_sino[2], su, sv);
+            THROW(WrongArgumentException, DctProjectionTransform, error_wrong_dim_u_sh);
+          }
+          if (dims_blob[2] < (dims_sino[2] + sv)) {
+            printf("Num sino: %lu, num blob: %lu, blobs UV size [%lu, %lu], sino UV size [%lu, %lu], sino shift UV [%lu, %lu]\n",
+                num_sino, b, dims_blob[0], dims_blob[2], dims_sino[0], dims_sino[2], su, sv);
+            THROW(WrongArgumentException, DctProjectionTransform, error_wrong_dim_v_sh);
+          }
         }
 
         if (s >= dims_sino[1]) {
@@ -850,6 +904,7 @@ namespace dct {
     for (size_t b = 0; b < tot_blobs; b++)
     {
       transforms_to_blobs[b].reserve(2 * tot_sinos);
+      transforms_to_blobs[b].use_shifts = this->use_shifts;
     }
 
     for (size_t num_sino = 0; num_sino < tot_sinos; ++num_sino)
@@ -864,7 +919,13 @@ namespace dct {
         const auto & c = metad_s.coeff[m];
 
         // (d_slice, s_vol, s_slice, coefficient)
-        transforms_to_blobs[b].emplace_back(p, num_sino, s, c);
+        if (metad_s.use_shifts) {
+          const auto & su = metad_s.shift_u[m];
+          const auto & sv = metad_s.shift_v[m];
+          transforms_to_blobs[b].emplace_back(p, num_sino, s, c, su, sv);
+        } else {
+          transforms_to_blobs[b].emplace_back(p, num_sino, s, c);
+        }
       }
     }
   }
@@ -904,25 +965,32 @@ namespace dct {
 
     for (size_t num_vol = 0; num_vol < complete_vols; num_vol++)
     {
-      funcs.emplace_back(std::function<void()>(std::bind(func, this, num_vol, size_t(0), lines_num)));
+      funcs.emplace_back(std::function<void()>(std::bind(func, this, num_vol, size_t(0), sinos_size_v)));
     }
 
     if (remaining_vols > 0) {
-      const size_t tot_theo_slabs = this->num_threads / remaining_vols + ((this->num_threads % remaining_vols) > 0);
-      const size_t tot_slabs = std::max(tot_theo_slabs, std::max(lines_num / 2, size_t(1)));
-      const size_t lines_per_slab = lines_num / tot_slabs + ((lines_num % tot_slabs) > 0);
+      if (this->use_shifts && func == &DctProjectionTransform<Type, Alloc, vector_size>::transform_sinos2blob) {
+        for (size_t num_vol = complete_vols; num_vol < tot_vols; num_vol++)
+        {
+          funcs.emplace_back(std::function<void()>(std::bind(func, this, num_vol, size_t(0), sinos_size_v)));
+        }
+      } else {
+        const size_t tot_theo_slabs = this->num_threads / remaining_vols + ((this->num_threads % remaining_vols) > 0);
+        const size_t tot_slabs = std::min(tot_theo_slabs, std::max(sinos_size_v / 2, size_t(1)));
+        const size_t lines_per_slab = sinos_size_v / tot_slabs + ((sinos_size_v % tot_slabs) > 0);
 
-      funcs.reserve(complete_vols + remaining_vols * tot_slabs);
+        funcs.reserve(complete_vols + remaining_vols * tot_slabs);
 
-      for (size_t num_blob = complete_vols; num_blob < tot_vols; num_blob++)
-      {
-        size_t slab_start = 0;
-        for (size_t num_slab = 0; num_slab < tot_slabs; ++num_slab) {
-          const size_t slab_end = std::min(slab_start + lines_per_slab, lines_num);
+        for (size_t num_vol = complete_vols; num_vol < tot_vols; num_vol++)
+        {
+          size_t slab_start = 0;
+          for (size_t num_slab = 0; num_slab < tot_slabs; ++num_slab) {
+            const size_t slab_end = std::min(slab_start + lines_per_slab, sinos_size_v);
 
-          funcs.emplace_back(std::function<void()>(std::bind(func, this, num_blob, slab_start, slab_end)));
+            funcs.emplace_back(std::function<void()>(std::bind(func, this, num_vol, slab_start, slab_end)));
 
-          slab_start = slab_end;
+            slab_start = slab_end;
+          }
         }
       }
     }
@@ -932,20 +1000,20 @@ namespace dct {
 
   template<typename Type, class Alloc, const size_t vector_size>
   void
-  DctProjectionTransform<Type, Alloc, vector_size>::transform_blobs2sino(const size_t & sino_num, const size_t & min_line, const size_t & max_line)
+  DctProjectionTransform<Type, Alloc, vector_size>::transform_blobs2sino(const size_t & sino_num, const size_t & min_v, const size_t & max_v)
   {
-    Type ALIGNED(vector_size) temp_line[line_length];
+    Type ALIGNED(vector_size) temp_line[sinos_size_u];
 
-    const size_t safe_min_line = std::max(min_line, size_t(0));
-    const size_t safe_max_line = std::min(max_line, lines_num);
+    const size_t safe_min_v = std::max(min_v, size_t(0));
+    const size_t safe_max_v = std::min(max_v, sinos_size_v);
 
     const DctProjectionTransformCoefficients<Type> & metad = transforms_to_sinos[sino_num];
 
-    for (size_t line_num = safe_min_line; line_num < safe_max_line; line_num++)
+    for (size_t v = safe_min_v; v < safe_max_v; v++)
     {
-      Type * __restrict const sino_base = sinos[sino_num].get_data() + line_num * sinos[sino_num].get_skip();
+      Type * __restrict const sino_base = sinos[sino_num].get_data() + v * sinos[sino_num].get_skip();
 
-      this->initialize_line(temp_line, line_length);
+      this->initialize_line(temp_line, sinos_size_u);
 
       for (size_t m = 0; m < metad.num_projections; m++)
       {
@@ -953,19 +1021,22 @@ namespace dct {
         const size_t & b = metad.src_vol[m];
         const size_t & s = metad.dest_slice[m];
 
-        OpBinaryPlusScaled<Type> op(metad.coeff[m]);
+        const size_t & su = metad.shift_u[m];
+        const size_t & sv = metad.shift_v[m];
+        const size_t b_shift = this->use_shifts ? (su + sv * blobs[b].get_skip()) : 0;
 
-        const Type * __restrict const blob_data_line = blobs[b].get_data() + line_length * p + line_num * blobs[b].get_skip();
+        OpBinaryPlusScaled<Type> op(metad.coeff[m]);
 
-        this->transform_line_binary(temp_line, blob_data_line, line_length, op);
+        const Type * __restrict const blob_data_line = blobs[b].get_data() + blobs_size_u * p + v * blobs[b].get_skip() + b_shift;
+        this->transform_line_binary(temp_line, blob_data_line, sinos_size_u, op);
 
         const bool is_last = (m+1) == metad.num_projections;
         if (is_last || metad.dest_slice[m+1] != s) {
-          Type * const __restrict sino_data_line = sino_base + line_length * s;
-          this->copy_line(sino_data_line, temp_line, line_length);
+          Type * const __restrict sino_data_line = sino_base + sinos_size_u * s;
+          this->copy_line(sino_data_line, temp_line, sinos_size_u);
 
           if (!is_last) {
-            this->initialize_line(temp_line, line_length);
+            this->initialize_line(temp_line, sinos_size_u);
           }
         }
       }
@@ -974,29 +1045,33 @@ namespace dct {
 
   template<typename Type, class Alloc, const size_t vector_size>
   void
-  DctProjectionTransform<Type, Alloc, vector_size>::transform_sinos2blob(const size_t & blob_num, const size_t & min_line, const size_t & max_line)
+  DctProjectionTransform<Type, Alloc, vector_size>::transform_sinos2blob(const size_t & blob_num, const size_t & min_v, const size_t & max_v)
   {
-    const size_t safe_min_line = std::max(min_line, size_t(0));
-    const size_t safe_max_line = std::min(max_line, lines_num);
+    const size_t safe_min_v = std::max(min_v, size_t(0));
+    const size_t safe_max_v = std::min(max_v, sinos_size_v);
 
     const DctProjectionTransformCoefficients<Type> & metad = transforms_to_blobs[blob_num];
 
-    for (size_t line_num = safe_min_line; line_num < safe_max_line; line_num++)
+    for (size_t v = safe_min_v; v < safe_max_v; v++)
     {
-      Type * __restrict const base_blob = blobs[blob_num].get_data() + line_num * blobs[blob_num].get_skip();
+      Type * const base_blob = blobs[blob_num].get_data() + v * blobs[blob_num].get_skip();
 
       for (size_t m = 0; m < metad.num_projections; m++)
       {
-        const size_t & sino_slice = metad.src_slice[m];
-        const size_t & sino_vol = metad.src_vol[m];
-        const size_t & blob_slice = metad.dest_slice[m];
+        const size_t & s = metad.src_slice[m];
+        const size_t & sino_num = metad.src_vol[m];
+        const size_t & p = metad.dest_slice[m];
+
+        const size_t & su = metad.shift_u[m];
+        const size_t & sv = metad.shift_v[m];
+        const size_t b_shift = this->use_shifts ? (su + sv * blobs[blob_num].get_skip()) : 0;
 
         OpBinaryPlusScaled<Type> op(metad.coeff[m]);
 
-        const Type * __restrict const sino_data_line = sinos[sino_vol].get_data() + line_num * sinos[sino_vol].get_skip() + line_length * sino_slice;
-        Type * __restrict const blob_data_line = base_blob + line_length * blob_slice;
+        const Type * __restrict const sino_data_line = sinos[sino_num].get_data() + v * sinos[sino_num].get_skip() + sinos_size_u * s;
+        Type * const blob_data_line = base_blob + blobs_size_u * p + b_shift;
 
-        this->transform_line_binary(blob_data_line, sino_data_line, line_length, op);
+        this->transform_line_binary(blob_data_line, sino_data_line, sinos_size_u, op);
       }
     }
   }
diff --git a/zUtil_Cxx/include/DctMatlabData.h b/zUtil_Cxx/include/DctMatlabData.h
index ae9be3a4..6ec98439 100644
--- a/zUtil_Cxx/include/DctMatlabData.h
+++ b/zUtil_Cxx/include/DctMatlabData.h
@@ -314,6 +314,9 @@ namespace dct {
     const mxArray * const proj_offsets_a = mxGetField(coeff, 0, "proj_offsets");
     const mxArray * const proj_coeffs_a = mxGetField(coeff, 0, "proj_coeffs");
 
+    const mxArray * const proj_shifts_uv_a = mxGetField(coeff, 0, "proj_shifts_uv");
+    output.use_shifts = proj_shifts_uv_a != nullptr;
+
     if (!sino_offsets_a) {
       THROW(WrongArgumentException, DctMatlabData,
           "Invalid Coeffs: 'sino_offsets' is missing!");
@@ -344,6 +347,10 @@ namespace dct {
       THROW(WrongArgumentException, DctMatlabData,
           "Invalid Coeffs: number of 'proj_coeffs' is different from 'blob_offsets'!");
     }
+    if (output.use_shifts && (num_elems * 2) != mxGetNumberOfElements(proj_shifts_uv_a)) {
+      THROW(WrongArgumentException, DctMatlabData,
+          "Invalid Coeffs: number of 'proj_shifts_uv' is different from 'blob_offsets'!");
+    }
 
     if (mxDOUBLE_CLASS != mxGetClassID(sino_offsets_a)) {
       THROW(WrongArgumentException, DctMatlabData,
@@ -361,6 +368,10 @@ namespace dct {
       THROW(WrongArgumentException, DctMatlabData,
           "Invalid Coeffs: type of 'proj_coeffs' should be 'double'!");
     }
+    if (output.use_shifts && mxDOUBLE_CLASS != mxGetClassID(proj_shifts_uv_a)) {
+      THROW(WrongArgumentException, DctMatlabData,
+          "Invalid Coeffs: type of 'proj_shifts_uv' should be 'double'!");
+    }
 
     output.resize(num_elems);
 
@@ -376,7 +387,19 @@ namespace dct {
     std::transform(projs_pos, projs_pos + num_elems, output.src_slice.begin(), op);
 
     const double * const coeffs = (const double *) mxGetData(proj_coeffs_a);
-    std::copy(coeffs, coeffs + num_elems, output.coeff.begin());
+    std::copy_n(coeffs, num_elems, output.coeff.begin());
+
+    if (output.use_shifts) {
+      const double * const shifts_uv = (const double *) mxGetData(proj_shifts_uv_a);
+      for (size_t ind = 0; ind < num_elems; ind++) {
+        // shifts always refer to inside the blobs
+        output.shift_u[ind] = shifts_uv[ind*2];
+        output.shift_v[ind] = shifts_uv[ind*2+1];
+      }
+    } else {
+      std::fill_n(output.shift_u.begin(), num_elems, 0);
+      std::fill_n(output.shift_v.begin(), num_elems, 0);
+    }
 
     return output;
   }
diff --git a/zUtil_Deformation/Gt6DVolumeToBlobProjector.m b/zUtil_Deformation/Gt6DVolumeToBlobProjector.m
index 9c3266ef..61a9b188 100644
--- a/zUtil_Deformation/Gt6DVolumeToBlobProjector.m
+++ b/zUtil_Deformation/Gt6DVolumeToBlobProjector.m
@@ -145,13 +145,24 @@ classdef Gt6DVolumeToBlobProjector < Gt6DVolumeProjector
                 blob_offsets = coeffs.blob_offsets(:);
                 proj_offsets = coeffs.proj_offsets(:);
                 proj_coeffs = coeffs.proj_coeffs(:);
+
+                if (isfield(coeffs, 'proj_shifts_uv'))
+                    shifts_uv = coeffs.proj_shifts_uv;
+                else
+                    shifts_uv = zeros(2, numel(proj_offsets));
+                end
+
                 for m = 1:numel(proj_offsets)
                     p = proj_offsets(m);
                     b = blob_offsets(m);
                     c = proj_coeffs(m);
                     s = sino_offsets(m);
 
-                    blobs{b}(:, p, :) = blobs{b}(:, p, :) + c * sino(:, s, :);
+                    blobs_us = (1:size(sino, 1)) + shifts_uv(1, m);
+                    blobs_vs = (1:size(sino, 3)) + shifts_uv(2, m);
+
+                    blobs{b}(blobs_us, p, blobs_vs) = blobs{b}(blobs_us, p, blobs_vs) + c * sino(:, s, :);
+%                     blobs{b}(:, p, :) = blobs{b}(:, p, :) + c * sino(:, s, :);
                 end
             catch mexc
                 fprintf('\n\nError!!\n')
@@ -175,13 +186,24 @@ classdef Gt6DVolumeToBlobProjector < Gt6DVolumeProjector
                 blob_offsets = coeffs.blob_offsets(:);
                 proj_offsets = coeffs.proj_offsets(:);
                 proj_coeffs = coeffs.proj_coeffs(:);
+
+                if (isfield(coeffs, 'proj_shifts_uv'))
+                    shifts_uv = coeffs.proj_shifts_uv;
+                else
+                    shifts_uv = zeros(2, numel(proj_offsets));
+                end
+
                 for m = 1:numel(proj_offsets)
                     p = proj_offsets(m);
                     b = blob_offsets(m);
                     c = proj_coeffs(m);
                     s = sino_offsets(m);
 
-                    sino(:, s, :) = sino(:, s, :) + c * blobs{b}(:, p, :);
+                    blobs_us = (1:sinogram_size(1)) + shifts_uv(1, m);
+                    blobs_vs = (1:sinogram_size(3)) + shifts_uv(2, m);
+
+                    sino(:, s, :) = sino(:, s, :) + c * blobs{b}(blobs_us, p, blobs_vs);
+%                     sino(:, s, :) = sino(:, s, :) + c * blobs{b}(:, p, :);
                 end
             catch mexc
                 fprintf('\n\nError!!\n')
-- 
GitLab