UsTK : Ultrasound ToolKit  version 2.0.1 under development (2023-12-07)
usVolumeProcessing.h
1 /****************************************************************************
2  *
3  * This file is part of the ustk software.
4  * Copyright (C) 2016 - 2017 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * ("GPL") version 2 as published by the Free Software Foundation.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ustk with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * This software was developed at:
17  * Inria Rennes - Bretagne Atlantique
18  * Campus Universitaire de Beaulieu
19  * 35042 Rennes Cedex
20  * France
21  *
22  * If you have questions regarding the use of this file, please contact
23  * Inria at ustk@inria.fr
24  *
25  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27  *
28  * Authors:
29  * Pierre Chatelain
30  * Jason Chevrie
31  *
32  *****************************************************************************/
33 
39 #ifndef __usVolumeProcessing_h_
40 #define __usVolumeProcessing_h_
41 
42 #include <visp3/core/vpConfig.h>
43 #ifdef VISP_HAVE_CPP11_COMPATIBILITY
44 #include <type_traits>
45 #endif
46 
47 #include <visp3/core/vpColVector.h>
48 #include <visp3/core/vpMath.h>
49 #include <visp3/core/vpMatrix.h>
50 
51 #include <visp3/ustk_core/usImage3D.h>
52 
58 class VISP_EXPORT usVolumeProcessing
59 {
60 public:
61  template <class Type1, class Type2>
62  static void absoluteDifference(const usImage3D<Type1> &src1, const usImage3D<Type1> &src2, usImage3D<Type2> &dst);
63 
64  template <class Type1, class Type2>
65  static double applyFilter(const usImage3D<Type1> &src, const usImage3D<Type2> &filter, unsigned int i, unsigned int j,
66  unsigned int k);
67 
68  template <class Type1, class Type2>
69  static void applyFilter(const usImage3D<Type1> &src, usImage3D<Type2> &dst, const usImage3D<double> &filter);
70 
71  template <class Type> static void computeBarycenter(const usImage3D<Type> &V, double &ic, double &jc, double &kc);
72 
73  template <class Type1, class Type2> static void derivativeI(const usImage3D<Type1> &src, usImage3D<Type2> &dst);
74 
75 #ifdef VISP_HAVE_CPP11_COMPATIBILITY
76  template <class Type1, class Type2 = typename std::conditional<std::is_arithmetic<Type1>::value, double, Type1>::type>
77  static Type2 derivativeI(const usImage3D<Type1> &V, unsigned int i, unsigned int j, unsigned int k);
78 #else
79  template <class Type>
80  static double derivativeI(const usImage3D<Type> &V, unsigned int i, unsigned int j, unsigned int k);
81 #endif
82 
83  template <class Type1, class Type2> static void derivativeJ(const usImage3D<Type1> &src, usImage3D<Type2> &dst);
84 
85 #ifdef VISP_HAVE_CPP11_COMPATIBILITY
86  template <class Type1, class Type2 = typename std::conditional<std::is_arithmetic<Type1>::value, double, Type1>::type>
87  static Type2 derivativeJ(const usImage3D<Type1> &V, unsigned int i, unsigned int j, unsigned int k);
88 #else
89  template <class Type>
90  static double derivativeJ(const usImage3D<Type> &V, unsigned int i, unsigned int j, unsigned int k);
91 #endif
92 
93  template <class Type1, class Type2> static void derivativeK(const usImage3D<Type1> &src, usImage3D<Type2> &dst);
94 
95 #ifdef VISP_HAVE_CPP11_COMPATIBILITY
96  template <class Type1, class Type2 = typename std::conditional<std::is_arithmetic<Type1>::value, double, Type1>::type>
97  static Type2 derivativeK(const usImage3D<Type1> &V, unsigned int i, unsigned int j, unsigned int k);
98 #else
99  template <class Type>
100  static double derivativeK(const usImage3D<Type> &V, unsigned int i, unsigned int j, unsigned int k);
101 #endif
102 
103  template <class Type1, class Type2>
104  static void difference(const usImage3D<Type1> &src1, const usImage3D<Type1> &src2, usImage3D<Type2> &dst);
105 
106  template <class Type>
107  static void frangi(const usImage3D<Type> &src, usImage3D<double> &dst, double a, double b, double c);
108 
109  template <class Type1, class Type2>
110  static void gaussianDerivativeI(const usImage3D<Type1> &src, usImage3D<Type2> &dst, double sigma,
111  unsigned int filter_size);
112 
113  template <class Type1, class Type2>
114  static void gaussianDerivativeJ(const usImage3D<Type1> &src, usImage3D<Type2> &dst, double sigma,
115  unsigned int filter_size);
116 
117  template <class Type1, class Type2>
118  static void gaussianDerivativeK(const usImage3D<Type1> &src, usImage3D<Type2> &dst, double sigma,
119  unsigned int filter_size);
120 
121  static usImage3D<double> generateGaussianDerivativeFilterI(double sigma, int size);
122 
123  static usImage3D<double> generateGaussianDerivativeFilterII(double sigma, int size);
124 
125  static usImage3D<double> generateGaussianDerivativeFilterIJ(double sigma, int size);
126 
127  static usImage3D<double> generateGaussianDerivativeFilterIK(double sigma, int size);
128 
129  static usImage3D<double> generateGaussianDerivativeFilterJ(double sigma, int size);
130 
131  static usImage3D<double> generateGaussianDerivativeFilterJJ(double sigma, int size);
132 
133  static usImage3D<double> generateGaussianDerivativeFilterJK(double sigma, int size);
134 
135  static usImage3D<double> generateGaussianDerivativeFilterK(double sigma, int size);
136 
137  static usImage3D<double> generateGaussianDerivativeFilterKK(double sigma, int size);
138 
139  template <class Type> static void gradient(const usImage3D<Type> &src, usImage3D<vpColVector> &dst);
140 
141  template <class Type> static void hessian(const usImage3D<Type> &src, usImage3D<vpMatrix> &dst);
142 
143  template <class Type> static Type max(const usImage3D<Type> &V);
144 
145  template <class Type> static Type min(const usImage3D<Type> &V);
146 
147  static void norm(const usImage3D<vpColVector> &src, usImage3D<double> &dst);
148 };
149 
150 /****************************************************************************
151 * Template implementations.
152 ****************************************************************************/
153 
159 template <class Type> Type usVolumeProcessing::max(const usImage3D<Type> &V)
160 {
161  const Type *p = V.getConstData();
162  Type max = *p;
163  for (unsigned int i = 1; i < V.getSize(); i++) {
164  Type val = *p;
165  if (val > max)
166  max = val;
167  p++;
168  }
169  return max;
170 }
171 
177 template <class Type> Type usVolumeProcessing::min(const usImage3D<Type> &V)
178 {
179  const Type *p = V.getConstData();
180  Type min = *p;
181  for (unsigned int i = 1; i < V.getSize(); i++) {
182  Type val = *p;
183  if (val < min)
184  min = val;
185  p++;
186  }
187  return min;
188 }
189 
199 template <class Type1, class Type2>
200 double usVolumeProcessing::applyFilter(const usImage3D<Type1> &src, const usImage3D<Type2> &filter, unsigned int i,
201  unsigned int j, unsigned int k)
202 {
203  unsigned int s_i = filter.getHeight();
204  unsigned int s_j = filter.getWidth();
205  unsigned int s_k = filter.getNumberOfFrames();
206  unsigned int m_i = s_i / 2;
207  unsigned int m_j = s_j / 2;
208  unsigned int m_k = s_k / 2;
209  Type2 v = Type2();
210 
211  if ((m_i < i) && (i < src.getHeight() - m_i) && (m_j < j) && (j < src.getWidth() - m_j) && (m_k < k) &&
212  (k < src.getNumberOfFrames() - m_k)) {
213  for (unsigned int k_it = 0; k_it < s_k; k_it++)
214  for (unsigned int j_it = 0; j_it < s_j; j_it++)
215  for (unsigned int i_it = 0; i_it < s_i; i_it++)
216  v += filter(i_it, j_it, k_it) * src(i + i_it - m_i, j + j_it - m_j, k + k_it - m_k);
217  }
218 
219  return v;
220 }
221 
228 template <class Type1, class Type2>
230  const usImage3D<double> &filter)
231 {
232  unsigned int s_i = filter.getHeight();
233  unsigned int s_j = filter.getWidth();
234  unsigned int s_k = filter.getNumberOfFrames();
235 
236  unsigned int height = src.getHeight();
237  unsigned int width = src.getWidth();
238  unsigned int nbFrames = src.getNumberOfFrames();
239  dst.resize(height, width, nbFrames);
240  dst.initData(0 * applyFilter(src, filter, s_i / 2, s_j / 2, s_k / 2));
241  for (unsigned int k = s_k; k < nbFrames - s_k; k++)
242  for (unsigned int j = s_j; j < width - s_j; j++)
243  for (unsigned int i = s_i; i < height - s_i; i++)
244  dst(i, j, k, applyFilter(src, filter, i, j, k));
245 }
246 
247 #ifdef VISP_HAVE_CPP11_COMPATIBILITY
256 template <class Type1, class Type2>
257 Type2 usVolumeProcessing::derivativeI(const usImage3D<Type1> &V, unsigned int i, unsigned int j, unsigned int k)
258 {
259  return ((Type2)V(i + 1, j, k) - (Type2)V(i - 1, j, k)) / 2.0;
260 }
261 
270 template <class Type1, class Type2>
271 Type2 usVolumeProcessing::derivativeJ(const usImage3D<Type1> &V, unsigned int i, unsigned int j, unsigned int k)
272 {
273  return ((Type2)V(i, j + 1, k) - (Type2)V(i, j - 1, k)) / 2.0;
274 }
275 
284 template <class Type1, class Type2>
285 Type2 usVolumeProcessing::derivativeK(const usImage3D<Type1> &V, unsigned int i, unsigned int j, unsigned int k)
286 {
287  return ((Type2)V(i, j, k + 1) - (Type2)V(i, j, k - 1)) / 2.0;
288 }
289 #else
298 template <class Type>
299 double usVolumeProcessing::derivativeI(const usImage3D<Type> &V, unsigned int i, unsigned int j, unsigned int k)
300 {
301  return ((double)V(i + 1, j, k) - (double)V(i - 1, j, k)) / 2.0;
302 }
303 
312 template <class Type>
313 double usVolumeProcessing::derivativeJ(const usImage3D<Type> &V, unsigned int i, unsigned int j, unsigned int k)
314 {
315  return ((double)V(i, j + 1, k) - (double)V(i, j - 1, k)) / 2.0;
316 }
317 
326 template <class Type>
327 double usVolumeProcessing::derivativeK(const usImage3D<Type> &V, unsigned int i, unsigned int j, unsigned int k)
328 {
329  return ((double)V(i, j, k + 1) - (double)V(i, j, k - 1)) / 2.0;
330 }
331 #endif
332 
341 template <class Type1, class Type2>
343  unsigned int filter_size)
344 {
345  usImage3D<double> filter = generateGaussianDerivativeFilterI(sigma, filter_size);
346  applyFilter(src, dst, filter);
347 }
348 
357 template <class Type1, class Type2>
359  unsigned int filter_size)
360 {
361  usImage3D<double> filter = generateGaussianDerivativeFilterJ(sigma, filter_size);
362  applyFilter(src, dst, filter);
363 }
364 
373 template <class Type1, class Type2>
375  unsigned int filter_size)
376 {
377  usImage3D<double> filter = generateGaussianDerivativeFilterK(sigma, filter_size);
378  applyFilter(src, dst, filter);
379 }
380 
386 template <class Type1, class Type2>
388 {
389  unsigned int height = src.getHeight();
390  unsigned int width = src.getWidth();
391  unsigned int nbFrames = src.getNumberOfFrames();
392  dst.resize(height, width, nbFrames);
393  Type2 zero = 0 * derivativeI(src, 1, 1, 1);
394  // Access in order k-j-i for performance
395  for (unsigned int k = 0; k < nbFrames; k++)
396  for (unsigned int j = 0; j < width; j++) {
397  dst(0, j, k, zero);
398  for (unsigned int i = 1; i < height - 1; i++)
399  dst(i, j, k, derivativeI(src, i, j, k));
400  dst(height - 1, j, k, zero);
401  }
402 }
403 
409 template <class Type1, class Type2>
411 {
412  unsigned int height = src.getHeight();
413  unsigned int width = src.getWidth();
414  unsigned int nbFrames = src.getNumberOfFrames();
415  dst.resize(height, width, nbFrames);
416  Type2 zero = 0 * derivativeJ(src, 1, 1, 1);
417  // Access in order k-j-i for performance
418  for (unsigned int k = 0; k < nbFrames; k++) {
419  for (unsigned int j = 1; j < width - 1; j++)
420  for (unsigned int i = 0; i < height; i++)
421  dst(i, j, k, derivativeJ(src, i, j, k));
422 
423  for (unsigned int i = 0; i < height; i++) {
424  dst(i, width - 1, k, zero);
425  dst(i, 0, k, zero);
426  }
427  }
428 }
429 
435 template <class Type1, class Type2>
437 {
438  unsigned int height = src.getHeight();
439  unsigned int width = src.getWidth();
440  unsigned int nbFrames = src.getNumberOfFrames();
441  dst.resize(height, width, nbFrames);
442  Type2 zero = 0 * derivativeK(src, 1, 1, 1);
443  // Access in order k-j-i for performance
444  for (unsigned int k = 1; k < nbFrames - 1; k++)
445  for (unsigned int j = 0; j < width; j++)
446  for (unsigned int i = 0; i < height; i++)
447  dst(i, j, k, derivativeK(src, i, j, k));
448 
449  for (unsigned int j = 0; j < width; j++)
450  for (unsigned int i = 0; i < height; i++) {
451  dst(i, j, 0, zero);
452  dst(i, j, nbFrames - 1, zero);
453  }
454 }
455 
461 template <class Type> void usVolumeProcessing::gradient(const usImage3D<Type> &src, usImage3D<vpColVector> &dst)
462 {
463  unsigned int height = src.getHeight();
464  unsigned int width = src.getWidth();
465  unsigned int nbFrames = src.getNumberOfFrames();
466  usImage3D<double> Gi, Gj, Gk;
467  derivativeI(src, Gi);
468  derivativeJ(src, Gj);
469  derivativeK(src, Gk);
470  dst.resize(height, width, nbFrames);
471  for (unsigned int i = 0; i < src.getSize(); i++) {
472  vpColVector v(3);
473  v[0] = Gi.getConstData()[i];
474  v[1] = Gj.getConstData()[i];
475  v[2] = Gk.getConstData()[i];
476  dst.getData()[i] = v;
477  }
478 }
479 
485 template <class Type> void usVolumeProcessing::hessian(const usImage3D<Type> &src, usImage3D<vpMatrix> &dst)
486 {
487  unsigned int height = src.getHeight();
488  unsigned int width = src.getWidth();
489  unsigned int nbFrames = src.getNumberOfFrames();
490  usImage3D<double> Gi, Gj, Gk, Gii, Gij, Gik, Gjj, Gjk, Gkk;
491  derivativeI(src, Gi);
492  derivativeJ(src, Gj);
493  derivativeK(src, Gk);
494  derivativeI(Gi, Gii);
495  derivativeI(Gj, Gij);
496  derivativeI(Gk, Gik);
497  derivativeJ(Gj, Gjj);
498  derivativeJ(Gk, Gjk);
499  derivativeK(Gk, Gkk);
500  dst.resize(height, width, nbFrames);
501  for (unsigned int i = 0; i < src.getSize(); i++) {
502  vpMatrix M(3, 3);
503  M[0][0] = Gii.getConstData()[i];
504  M[0][1] = Gij.getConstData()[i];
505  M[0][2] = Gik.getConstData()[i];
506  M[1][0] = Gij.getConstData()[i];
507  M[1][1] = Gjj.getConstData()[i];
508  M[1][2] = Gjk.getConstData()[i];
509  M[2][0] = Gik.getConstData()[i];
510  M[2][1] = Gjk.getConstData()[i];
511  M[2][2] = Gkk.getConstData()[i];
512  dst.getData()[i] = M;
513  }
514 }
515 
529 template <class Type>
530 void usVolumeProcessing::frangi(const usImage3D<Type> &src, usImage3D<double> &dst, double a, double b, double c)
531 {
532  unsigned int height = src.getHeight();
533  unsigned int width = src.getWidth();
534  unsigned int nbFrames = src.getNumberOfFrames();
536  hessian(src, H);
537  dst.resize(height, width, nbFrames);
538  for (unsigned int i = 0; i < src.getSize(); i++) {
539  vpColVector evalues = H.getConstData()[i].eigenValues();
540 
541  // Sort eigenvalues
542  if (vpMath::abs(evalues[0]) > vpMath::abs(evalues[1]))
543  std::swap(evalues[0], evalues[1]);
544  if (vpMath::abs(evalues[1]) > vpMath::abs(evalues[2]))
545  std::swap(evalues[1], evalues[2]);
546  if (vpMath::abs(evalues[0]) > vpMath::abs(evalues[1]))
547  std::swap(evalues[0], evalues[1]);
548 
549  double v;
550  if ((evalues[1] >= 0.0) || (evalues[2] >= 0.0))
551  v = 0.0;
552  else {
553  double Rb = vpMath::abs(evalues[0]) / sqrt(vpMath::abs(evalues[1] * evalues[2]));
554  double Ra = vpMath::abs(evalues[1] / evalues[2]);
555  double S = evalues.frobeniusNorm();
556 
557  v = (1.0 - exp(-vpMath::sqr(Ra) / (2.0 * vpMath::sqr(a)))) * exp(-vpMath::sqr(Rb) / (2.0 * vpMath::sqr(b))) *
558  (1.0 - exp(-vpMath::sqr(S) / (2.0 * vpMath::sqr(c))));
559  }
560  dst.getData()[i] = v;
561  }
562 }
563 
570 template <class Type1, class Type2>
572 {
573  unsigned int height = src1.getHeight();
574  unsigned int width = src1.getWidth();
575  unsigned int nbFrames = src1.getNumberOfFrames();
576  if (height != src2.getHeight() || width != src2.getWidth() || nbFrames != src2.getNumberOfFrames())
577  throw vpException(vpException::dimensionError, "usVolumeProcessing::difference: mismatched volumes dimensions");
578  unsigned int n = src1.getSize();
579  dst.resize(height, width, nbFrames);
580  for (unsigned int i = 0; i < n; i++)
581  dst.getData()[i] = src1.getConstData()[i] - src2.getConstData()[i];
582 }
583 
590 template <class Type1, class Type2>
592  usImage3D<Type2> &dst)
593 {
594  unsigned int height = src1.getHeight();
595  unsigned int width = src1.getWidth();
596  unsigned int nbFrames = src1.getNumberOfFrames();
597  if (height != src2.getHeight() || width != src2.getWidth() || nbFrames != src2.getNumberOfFrames())
598  throw vpException(vpException::dimensionError,
599  "usVolumeProcessing::absoluteDifference: mismatched volumes dimensions");
600  unsigned int n = src1.getSize();
601  dst.resize(height, width, nbFrames);
602  for (unsigned int i = 0; i < n; i++)
603  dst.getData()[i] = vpMath::abs(src1.getConstData()[i] - src2.getConstData()[i]);
604 }
605 
613 template <class Type>
614 void usVolumeProcessing::computeBarycenter(const usImage3D<Type> &V, double &ic, double &jc, double &kc)
615 {
616  unsigned int height = V.getHeight();
617  unsigned int width = V.getWidth();
618  unsigned int nbFrames = V.getNumberOfFrames();
619  Type V_sum = 0 * V(0, 0, 0);
620  double i_c = 0.0;
621  double j_c = 0.0;
622  double k_c = 0.0;
623  for (unsigned int k = 0; k < nbFrames; k++)
624  for (unsigned int j = 0; j < width; j++)
625  for (unsigned int i = 0; i < height; i++) {
626  Type val = V(i, j, k);
627  i_c += i * val;
628  j_c += j * val;
629  k_c += k * val;
630  V_sum += val;
631  }
632  ic = i_c / V_sum;
633  jc = j_c / V_sum;
634  kc = k_c / V_sum;
635 }
636 
637 #endif // __usVolumeProcessing_h_
Representation of a physical image volume.
Definition: usImage3D.h:61
unsigned int getNumberOfFrames() const
Definition: usImage3D.h:137
unsigned int getSize() const
Definition: usImage3D.h:143
Type * getData()
Definition: usImage3D.h:110
void resize(unsigned int height, unsigned int width, unsigned int numberOfFrames)
Definition: usImage3D.h:376
Type * getConstData() const
Definition: usImage3D.h:104
void initData(Type value)
Definition: usImage3D.h:367
unsigned int getHeight() const
Definition: usImage3D.h:131
unsigned int getWidth() const
Definition: usImage3D.h:125
Processing tools (derivative, filtering...) for the usImage3D class.
static void hessian(const usImage3D< Type > &src, usImage3D< vpMatrix > &dst)
static void frangi(const usImage3D< Type > &src, usImage3D< double > &dst, double a, double b, double c)
static Type min(const usImage3D< Type > &V)
static usImage3D< double > generateGaussianDerivativeFilterJ(double sigma, int size)
static void gaussianDerivativeJ(const usImage3D< Type1 > &src, usImage3D< Type2 > &dst, double sigma, unsigned int filter_size)
static void gradient(const usImage3D< Type > &src, usImage3D< vpColVector > &dst)
static void derivativeI(const usImage3D< Type1 > &src, usImage3D< Type2 > &dst)
static usImage3D< double > generateGaussianDerivativeFilterI(double sigma, int size)
static Type max(const usImage3D< Type > &V)
static void derivativeJ(const usImage3D< Type1 > &src, usImage3D< Type2 > &dst)
static void difference(const usImage3D< Type1 > &src1, const usImage3D< Type1 > &src2, usImage3D< Type2 > &dst)
static void derivativeK(const usImage3D< Type1 > &src, usImage3D< Type2 > &dst)
static void absoluteDifference(const usImage3D< Type1 > &src1, const usImage3D< Type1 > &src2, usImage3D< Type2 > &dst)
static void gaussianDerivativeI(const usImage3D< Type1 > &src, usImage3D< Type2 > &dst, double sigma, unsigned int filter_size)
static void gaussianDerivativeK(const usImage3D< Type1 > &src, usImage3D< Type2 > &dst, double sigma, unsigned int filter_size)
static double applyFilter(const usImage3D< Type1 > &src, const usImage3D< Type2 > &filter, unsigned int i, unsigned int j, unsigned int k)
static void computeBarycenter(const usImage3D< Type > &V, double &ic, double &jc, double &kc)
static usImage3D< double > generateGaussianDerivativeFilterK(double sigma, int size)