UsTK : Ultrasound ToolKit  version 2.0.1 under development (2023-12-07)
usPreScanToPostScan2DConverter.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  * Pierre Chatelain
30  *
31  *****************************************************************************/
32 
33 #include <visp/vpMath.h>
34 #include <visp3/ustk_core/usPreScanToPostScan2DConverter.h>
35 
37 
39 
48  const int BModeSampleNumber, const int scanLineNumber)
49 {
50  // check resolution to avoir errors
51  if (inputSettings.getHeightResolution() == 0.0 || inputSettings.getWidthResolution() == 0.0)
52  throw(vpException(vpException::notInitialized, "Please fill the post-scan resolution before init the conversion."));
53 
54  if (inputSettings.isTransducerConvex()) {
55  m_scanLineNumber = scanLineNumber;
56  m_BModeSampleNumber = BModeSampleNumber;
57  m_xResolution = inputSettings.getWidthResolution();
58  m_yResolution = inputSettings.getHeightResolution();
59  m_settings = inputSettings;
60 
61  double APitch = inputSettings.getDepth() / (double)(BModeSampleNumber);
62  double LPitch = inputSettings.getFieldOfView() * inputSettings.getTransducerRadius() / (double)(scanLineNumber - 1);
63 
64  double r_min = inputSettings.getTransducerRadius();
65  double r_max = (inputSettings.getTransducerRadius() + APitch * BModeSampleNumber);
66  double t_min = -((double)(scanLineNumber - 1) * LPitch) / (2.0 * inputSettings.getTransducerRadius());
67  double t_max = -t_min;
68  double x_min = r_min * cos(t_min);
69  double x_max = r_max;
70  double y_min = r_max * sin(t_min);
71  double y_max = r_max * sin(t_max);
72 
73  m_height = vpMath::round((x_max - x_min) / m_yResolution);
74  m_width = vpMath::round((y_max - y_min) / m_xResolution);
75 
76  m_rMap.resize(m_height, m_width);
77  m_tMap.resize(m_height, m_width);
78 
79  double x, y;
80  for (unsigned int i = 0; i < m_height; ++i) {
81  for (unsigned int j = 0; j < m_width; ++j) {
82  x = x_min + i * m_yResolution;
83  y = y_min + j * m_xResolution;
84  m_rMap[i][j] = (sqrt(x * x + y * y) - inputSettings.getTransducerRadius()) / APitch;
85  m_tMap[i][j] = atan2(y, x) * inputSettings.getTransducerRadius() / LPitch + (scanLineNumber - 1) / 2.0;
86  }
87  }
88  } else {
89  m_scanLineNumber = scanLineNumber;
90  m_BModeSampleNumber = BModeSampleNumber;
91  m_xResolution = inputSettings.getWidthResolution();
92  m_yResolution = inputSettings.getHeightResolution();
93  m_settings = inputSettings;
94 
95  double APitch = inputSettings.getDepth() / (double)(BModeSampleNumber);
96  m_height = vpMath::round((APitch * BModeSampleNumber) / m_yResolution);
97  m_width = vpMath::round(inputSettings.getScanLinePitch() * (scanLineNumber - 1) / m_xResolution);
98 
99  m_rMap.resize(m_height, m_width);
100  m_tMap.resize(m_height, m_width);
101 
102  for (unsigned int i = 0; i < m_height; ++i) {
103  for (unsigned int j = 0; j < m_width; ++j) {
104  m_rMap[i][j] = j;
105  m_tMap[i][j] = i;
106  }
107  }
108  }
109 
110  m_initDone = true;
111 }
112 
122 void usPreScanToPostScan2DConverter::init(const usTransducerSettings &inputSettings, const int BModeSampleNumber,
123  const int scanLineNumber, const double xResolution, const double yResolution)
124 {
125  if (inputSettings.isTransducerConvex()) {
126  m_scanLineNumber = scanLineNumber;
127  m_BModeSampleNumber = BModeSampleNumber;
128  m_xResolution = xResolution;
129  m_yResolution = yResolution;
130  m_settings = inputSettings;
131 
132  double APitch = inputSettings.getDepth() / (double)(BModeSampleNumber);
133  double LPitch = inputSettings.getFieldOfView() * inputSettings.getTransducerRadius() / (double)(scanLineNumber - 1);
134 
135  double r_min = inputSettings.getTransducerRadius();
136  double r_max = (inputSettings.getTransducerRadius() + APitch * BModeSampleNumber);
137  double t_min = -((double)(scanLineNumber - 1) * LPitch) / (2.0 * inputSettings.getTransducerRadius());
138  double t_max = -t_min;
139  double x_min = r_min * cos(t_min);
140  double x_max = r_max;
141  double y_min = r_max * sin(t_min);
142  double y_max = r_max * sin(t_max);
143 
144  m_height = vpMath::round((x_max - x_min) / m_yResolution);
145  m_width = vpMath::round((y_max - y_min) / m_xResolution);
146 
147  m_rMap.resize(m_height, m_width);
148  m_tMap.resize(m_height, m_width);
149 
150  double x, y;
151  for (unsigned int i = 0; i < m_height; ++i) {
152  for (unsigned int j = 0; j < m_width; ++j) {
153  x = x_min + i * m_yResolution;
154  y = y_min + j * m_xResolution;
155  m_rMap[i][j] = (sqrt(x * x + y * y) - inputSettings.getTransducerRadius()) / APitch;
156  m_tMap[i][j] = atan2(y, x) * inputSettings.getTransducerRadius() / LPitch + (scanLineNumber - 1) / 2.0;
157  }
158  }
159  } else {
160  m_scanLineNumber = scanLineNumber;
161  m_BModeSampleNumber = BModeSampleNumber;
162  m_xResolution = xResolution;
163  m_yResolution = yResolution;
164  m_settings = inputSettings;
165 
166  double APitch = inputSettings.getDepth() / (double)(BModeSampleNumber);
167  m_height = vpMath::round(inputSettings.getDepth() / m_yResolution);
168  m_width = vpMath::round(inputSettings.getScanLinePitch() * (scanLineNumber - 1) / m_xResolution);
169 
170  m_rMap.resize(m_height, m_width);
171  m_tMap.resize(m_height, m_width);
172 
173  double ratio1 = m_xResolution / inputSettings.getScanLinePitch();
174  double ratio2 = m_yResolution / APitch;
175 
176  for (unsigned int i = 0; i < m_height; ++i) {
177  for (unsigned int j = 0; j < m_width; ++j) {
178  m_rMap[i][j] = i * ratio2;
179  m_tMap[i][j] = j * ratio1;
180  }
181  }
182  }
183 
184  m_initDone = true;
185 }
186 
195  usImagePostScan2D<unsigned char> &postScanImage, double xResolution,
196  double yResolution)
197 {
198  // if user specified the resolution wanted
199  if (xResolution != 0. && yResolution != 0.) {
200  init(preScanImage, preScanImage.getBModeSampleNumber(), preScanImage.getScanLineNumber(), xResolution, yResolution);
201  }
202 
203  // check if init is done
204  if (!m_initDone) {
205  if (preScanImage.isTransducerConvex()) {
206  double resolution = preScanImage.getAxialResolution();
207  init(preScanImage, preScanImage.getBModeSampleNumber(), preScanImage.getScanLineNumber(), resolution, resolution);
208  } else {
209  init(preScanImage, preScanImage.getBModeSampleNumber(), preScanImage.getScanLineNumber(),
210  preScanImage.getScanLinePitch(), preScanImage.getAxialResolution());
211  }
212  }
213 
214  postScanImage.resize(m_height, m_width);
215  for (unsigned int i = 0; i < m_height; ++i)
216  for (unsigned int j = 0; j < m_width; ++j) {
217  double u = m_rMap[i][j];
218  double v = m_tMap[i][j];
219  postScanImage(i, j, (unsigned char)interpolateLinear(preScanImage, u, v));
220  }
221  // saving settings in postScanImage
222  postScanImage.setHeightResolution(m_yResolution);
223  postScanImage.setWidthResolution(m_xResolution);
224  postScanImage.setScanLineNumber(m_scanLineNumber);
225  postScanImage.setTransducerConvexity(preScanImage.isTransducerConvex());
226  postScanImage.setScanLinePitch(m_settings.getScanLinePitch());
227  postScanImage.setTransducerRadius(m_settings.getTransducerRadius());
228  postScanImage.setDepth(m_settings.getDepth());
229 }
230 
231 double usPreScanToPostScan2DConverter::interpolateLinear(const vpImage<unsigned char> &I, double x, double y)
232 {
233  int x1 = (int)floor(x);
234  int x2 = (int)ceil(x);
235  int y1 = (int)floor(y);
236  int y2 = (int)ceil(y);
237 
238  if ((0 <= x) && (x < I.getHeight()) && (0 <= y) && (y < I.getWidth())) {
239  double val1, val2;
240  // Check whether the indices are within the image extent
241  if (x1 < 0)
242  ++x1;
243  if (y1 < 0)
244  ++y1;
245  if (x2 >= static_cast<int>(I.getHeight()))
246  --x2;
247  if (y2 >= static_cast<int>(I.getWidth()))
248  --y2;
249 
250  // Check whether the target is on the grid
251  if (x1 == x2) {
252  val1 = I(x1, y1);
253  val2 = I(x1, y2);
254  } else {
255  val1 = (x2 - x) * I(x1, y1) + (x - x1) * I(x2, y1);
256  val2 = (x2 - x) * I(x1, y2) + (x - x1) * I(x2, y2);
257  }
258  if (y1 == y2)
259  return val1;
260  else
261  return (y2 - y) * val1 + (y - y1) * val2;
262  }
263  return 0.0;
264 }
void setHeightResolution(double heightResolution)
double getHeightResolution() const
void setWidthResolution(double widthResolution)
double getWidthResolution() const
unsigned int getBModeSampleNumber() const
void convert(const usImagePreScan2D< unsigned char > &preScanImage, usImagePostScan2D< unsigned char > &postScanImage, double xResolution=0., double yResolution=0.)
void init(const usImagePostScan2D< unsigned char > &inputSettings, const int BModeSampleNumber, const int scanLineNumber)
Generic class for 2D ultrasound data common settings associated to the type of probe transducer used ...
void setTransducerConvexity(const bool isTransducerConvex)
void setDepth(double depth)
void setScanLinePitch(const double scanLinePitch)
void setTransducerRadius(const double transducerRadius)
double getTransducerRadius() const
unsigned int getScanLineNumber() const
void setScanLineNumber(unsigned int scanLineNumber)