UsTK : Ultrasound ToolKit  version 2.0.1 under development (2023-12-07)
usElastography.cpp
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  * Pedro Patlan Rosales
30  * Marc Pouliquen
31  *
32  *****************************************************************************/
33 
34 #include <visp3/ustk_elastography/usElastography.h>
35 
36 #if defined(USTK_HAVE_FFTW)
37 
42 {
43  // Using default percentage of LSQ strain
44  // If you want to change this value use
45  // setLSQpercentage(double per);
46  m_Lsqper = 0.01; // 0.08 BMA //Just OF 0.01
47  // Frame rate in fps
48  m_FPS = 24.0;
49  // Sampling frequency
50  m_samplingFrequency = 40e6;
51  // speed of the sound
52  m_c = 1540.0;
53  m_isloadPre = false;
54  m_isloadPost = false;
55  m_setROI = false;
56  // Using OF by default
57  m_mEstimatior = OF;
58 #if defined(USTK_HAVE_ARMADILLO) && (ARMA_VERSION_MAJOR > 6) && (ARMA_VERSION_MAJOR > 700)
59  m_ME = usMotionEstimation();
60 #endif
61 
62  m_decimationFactor = 10;
63 
64  // Avoiding leaking memory with the convolutions on cpu
65  for (uint i = 0; i < 6; i++)
66  cC.push_back(new usConvolution2d);
67 }
68 
76 {
77  setPreCompression(Pre);
78  setPostCompression(Post);
79  m_Lsqper = 0.01;
80  m_FPS = 24.0;
81  m_samplingFrequency = 40e6;
82  m_c = 1540.0;
83  m_setROI = false;
84  m_mEstimatior = OF;
85 
86  for (uint i = 0; i < 6; i++)
87  cC.push_back(new usConvolution2d);
88 }
89 
94 {
95  // Delete the convolution objects
96  for (uint i = 0; i < 6; i++)
97  delete cC.at(i);
98 }
99 
105 {
106  m_Precomp = Pre;
107  m_isloadPre = true;
108 }
109 
115 {
116  m_Postcomp = Post;
117  m_isloadPost = true;
118 }
119 
124 void usElastography::setLSQpercentage(double per) { m_Lsqper = per; }
125 
130 void usElastography::setFPS(double fps) { m_FPS = fps; }
131 
136 void usElastography::setSamplingFrequency(double samplingFrequency) { m_samplingFrequency = samplingFrequency; }
137 
144 {
145  if (!m_isloadPre) {
146  setPreCompression(image);
147  } else {
148  if (m_isloadPost)
149  setPreCompression(m_Postcomp);
150  setPostCompression(image);
151  }
152 }
153 
161 void usElastography::setROI(int tx, int ty, int tw, int th)
162 {
163  m_ix = tx;
164  m_iy = ty;
165  m_rw = tw;
166  m_rh = th;
167  m_setROI = true;
168 }
169 
175 void usElastography::updateROIPos(int tx, int ty)
176 {
177  m_ix = tx;
178  m_iy = ty;
179 }
180 
181 usImageRF2D<short int> usElastography::usImageRF_ROI(const usImageRF2D<short int> &M, uint r, uint c, uint nrows,
182  uint ncols)
183 {
184  uint rnrows = r + nrows;
185  uint cncols = c + ncols;
186  usImageRF2D<short int> t_Mout;
187  assert(rnrows < M.getHeight() || cncols < M.getWidth());
188  t_Mout.resize(nrows, ncols);
189  for (unsigned int i = r; i < rnrows; i++)
190  for (unsigned int j = c; j < cncols; j++)
191  t_Mout(i - r, j - c, M(i, j));
192 
193  return t_Mout;
194 }
195 
200 vpImage<unsigned char> usElastography::run()
201 {
202 
203  if (m_isloadPre == true && m_isloadPost == true && m_setROI == true) {
204  m_PreROI = usImageRF_ROI(m_Precomp, m_iy, m_ix, m_rh, m_rw);
205  m_PostROI = usImageRF_ROI(m_Postcomp, m_iy, m_ix, m_rh, m_rw);
206  assert(m_PreROI.getWidth() == m_PostROI.getWidth());
207  assert(m_PreROI.getHeight() == m_PostROI.getHeight());
208  if (m_mEstimatior == BMA_TAYLOR) {
209 #if defined(USTK_HAVE_ARMADILLO) && (ARMA_VERSION_MAJOR > 6) && (ARMA_VERSION_MAJOR > 700)
210  // Step 0: BMA
211  m_ME.init(m_PreROI, m_PostROI, 2, 20, 2, 120);
212  m_ME.run();
213  // U = m_ME.getU_vp() * (m_c * (m_PRF / (2.0 * m_samplingFrequency)));
214  V = m_ME.getV_vp() * (m_c * (m_FPS / (2.0 * m_samplingFrequency)));
215  m_h_m = m_PreROI.getHeight();
216  m_w_m = m_PreROI.getWidth();
217 #else
218  throw vpException(vpException::fatalError,
219  "usElastography : cannot use block-matching algorithm, armadillo is missing.");
220 #endif
221  } else {
222  // Step 1: Numerical gradients
223  vpMatrix Fx = usSignalProcessing::getXGradient(m_PreROI);
224  vpMatrix Fy = usSignalProcessing::getYGradient(m_PreROI);
225  // Difference
226  // vpMatrix Dt = m_Postcomp-m_Precomp;
227  vpMatrix Dt = usSignalProcessing::Difference(m_PostROI, m_PreROI);
228  // Step 2: computing the quadratic functions
229  vpMatrix dx2 = usSignalProcessing::HadamardProd(Fx, Fx);
230  vpMatrix dy2 = usSignalProcessing::HadamardProd(Fy, Fy);
231  vpMatrix dxy = usSignalProcessing::HadamardProd(Fx, Fy);
232  vpMatrix dtx = usSignalProcessing::HadamardProd(Dt, Fx);
233  vpMatrix dty = usSignalProcessing::HadamardProd(Dt, Fy);
234  // Step 3: blur the 5 images
235  vpMatrix h_gauss = usSignalProcessing::GaussianFilter(15, 5, 7.5); //(51,35,5); //21 good-phantom
236 
237  vpMatrix gdx2 = cC[0]->run(dx2, h_gauss);
238  vpMatrix gdy2 = cC[1]->run(dy2, h_gauss);
239  vpMatrix gdxy = cC[2]->run(dxy, h_gauss);
240  vpMatrix gdty = cC[3]->run(dty, h_gauss);
241  vpMatrix gdtx = cC[4]->run(dtx, h_gauss);
242 
243  // Step 4: compute the u, v components of the displacement
244  // usign windows of size Wsizex, Wsizey
245  m_h_m = gdx2.getRows();
246  m_w_m = gdx2.getCols();
247  int Wsizex = vpMath::round(0.1 * m_w_m); // 0.07
248  int Wsizey = vpMath::round(0.1 * m_h_m); // 0.07
249  int Wincx = vpMath::round(0.75 * Wsizex);
250  int Wincy = vpMath::round(0.75 * Wsizey);
251  int h_w = vpMath::round((double)(m_h_m - Wsizey) / (double)Wincy);
252  int w_w = vpMath::round((double)(m_w_m - Wsizex) / (double)Wincx);
253 
254  assert(w_w > 1);
255  assert(h_w > 1);
256 
257  // U.resize(h_w, w_w);
258  V.resize(h_w, w_w);
259  vpMatrix b(2, 1);
260  vpMatrix M(2, 2);
261  vpMatrix X(2, 1);
262  uint k = 0, l;
263  for (uint n = 0; n < (uint)(m_w_m - Wsizex - Wincx); n += Wincx) {
264  l = 0;
265  for (uint m = 0; m < (uint)(m_h_m - Wsizey - Wincy); m += Wincy) {
266  double sgdx2_w = 0.0;
267  double sgdy2_w = 0.0;
268  double sgdxy_w = 0.0;
269  double sgdtx_w = 0.0;
270  double sgdty_w = 0.0;
271  for (uint i = m; i < (m + Wsizey); i++) {
272  for (uint j = n; j < (n + Wsizex); j++) {
273  sgdx2_w += gdx2[i][j];
274  sgdy2_w += gdy2[i][j];
275  sgdxy_w += gdxy[i][j];
276  sgdtx_w += gdtx[i][j];
277  sgdty_w += gdty[i][j];
278  }
279  }
280  M.data[0] = sgdx2_w;
281  M.data[1] = sgdxy_w;
282  M.data[2] = sgdxy_w;
283  M.data[3] = sgdy2_w;
284  b.data[0] = -sgdtx_w;
285  b.data[1] = -sgdty_w;
286 
287  X = M.pseudoInverse() * b;
288  // U[l][k] = X.data[0] * (m_c * (m_PRF / (2.0 * m_samplingFrequency))); //lateral displacements
289  V[l][k] = X.data[1] * (m_c * (m_FPS / (2.0 * m_samplingFrequency))); // axial displacements
290  l++;
291  }
292  k++;
293  }
294  }
295 
296  vpMatrix vV(m_h_m, m_w_m, true);
297  vV = usSignalProcessing::BilinearInterpolation(V, m_w_m, m_h_m);
300  double kappa = (2.0 * m_samplingFrequency) / (m_c * m_FPS);
301  double d_m = m_Lsqper * m_h_m; // 20.0; //Good value in phantom
302  double n = d_m + 1;
303  // Filter kernel
304  vpMatrix h((uint)n + 1, 1, true);
305  double xi = kappa * 12.0 / (n * (n * n - 1));
306  uint j = 0;
307  for (int i = (int)n; i >= 0; i--) {
308  h[j][0] = xi * (i - (n + 1) / 2.0);
309  j++;
310  }
311 
313  vpMatrix strainMatrix = cC[5]->run(vV, h);
314 
315  m_StrainMap.resize((unsigned int)(strainMatrix.getRows() / (double)m_decimationFactor), strainMatrix.getCols());
316 
317  m_min_str = std::abs(strainMatrix.getMinValue());
318  m_max_str = std::abs(strainMatrix.getMaxValue());
319  m_max_abs = (m_min_str > m_max_str) ? m_min_str : m_max_str;
320  for (unsigned int xIndex = 0; xIndex < m_StrainMap.getCols(); ++xIndex) {
321  for (unsigned int yIndex = 0; yIndex < m_StrainMap.getRows(); ++yIndex) {
322  m_StrainMap[yIndex][xIndex] =
323  std::isnan(strainMatrix[yIndex * m_decimationFactor][xIndex])
324  ? 0.0
325  : 254 * (fabs(strainMatrix[yIndex * m_decimationFactor][xIndex]) / (m_max_abs));
326  }
327  }
328  }
329  return m_StrainMap;
330 }
331 #endif // QT
Convolution process for elastography puropse, based on fftw thirdparty library.
vpImage< unsigned char > run()
virtual ~usElastography()
void updateROIPos(int tx, int ty)
void setLSQpercentage(double per)
void setFPS(double fps)
void setROI(int tx, int ty, int tw, int th)
void setPreCompression(const usImageRF2D< short > &Pre)
void setSamplingFrequency(double samplingFrequency)
void updateRF(const usImageRF2D< short int > &image)
void setPostCompression(const usImageRF2D< short > &Post)
unsigned int getHeight() const
Definition: usImageRF2D.h:412
void resize(const unsigned int height, const unsigned int width)
Definition: usImageRF2D.h:328
unsigned int getWidth() const
Definition: usImageRF2D.h:424
Class containing a set of static methods to compute various processes on RF images (gradients,...
static vpMatrix BilinearInterpolation(vpMatrix In, uint newW, uint newH)
Bilinear Interpolation.
static vpMatrix GaussianFilter(int height, int width, double sigma)
Gaussian Kernel generator.
static vpMatrix Difference(const usImageRF2D< short int > &A, const usImageRF2D< short int > &B)
static vpMatrix HadamardProd(vpMatrix matrix1, vpMatrix matrix2)
Element-wise product.
static vpMatrix getXGradient(const usImageRF2D< short > &image)
Computation of gradients.
static vpMatrix getYGradient(const usImageRF2D< short int > &image)