PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusRfToBrightnessConvert.cxx
Go to the documentation of this file.
1 /*=Plus=header=begin======================================================
2 Program: Plus
3 Copyright (c) Laboratory for Percutaneous Surgery. All rights reserved.
4 See License.txt for details.
5 =========================================================Plus=header=end*/
6 
7 #include "PlusConfigure.h"
8 
10 
11 #include "vtkImageData.h"
12 #include "vtkInformation.h"
13 #include "vtkInformationVector.h"
14 #include "vtkObjectFactory.h"
15 #include "vtkStreamingDemandDrivenPipeline.h"
16 #include "vtkMath.h"
17 
18 #include <math.h>
19 
21 
22 const double MIN_BRIGHTNESS_VALUE = 0.0;
23 const double MAX_BRIGHTNESS_VALUE = 255.0;
24 
25 //----------------------------------------------------------------------------
27 {
28  this->ImageType = US_IMG_TYPE_XX;
29  this->BrightnessScale = 10.0;
30  this->NumberOfHilbertFilterCoeffs = 64;
31 }
32 
33 //----------------------------------------------------------------------------
35 {
36 }
37 
38 //----------------------------------------------------------------------------
40  vtkInformationVector** inputVector,
41  vtkInformationVector* outputVector)
42 {
43  // get the info objects
44  vtkInformation* outInfo = outputVector->GetInformationObject(0);
45  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
46 
47  int inExt[6] = {0};
48  inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), inExt);
49 
50  // Set the output extent to be the same as the input extent by default
51  int outExt[6] = {0};
52  inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), outExt);
53 
54  // Update the output image extent depending on the RF encoding type
55  switch (this->ImageType)
56  {
57  case US_IMG_BRIGHTNESS:
58  {
59  // output is the same as the input
60  }
61  break;
62  case US_IMG_RF_I_LINE_Q_LINE:
63  {
64  // RF data: IIIIII..., QQQQQQ....
65  // B-mode data: BBBBBB
66  // => number of rows in the output image is half of the rows in the input image
67  int numberOfBmodeRows = (inExt[3] - inExt[2] + 1) / 2;
68  outExt[2] = inExt[2] / 2;
69  outExt[3] = outExt[2] + numberOfBmodeRows - 1;
70  }
71  break;
72  case US_IMG_RF_REAL:
73  {
74  // RF data: III..., III...
75  // B-mode data: BBB..., BBB...
76  // => the output image size is the same as the input image size
77  // keep the default extent
78  }
79  break;
80  case US_IMG_RF_IQ_LINE:
81  {
82  // RF data: IQIQIQ....., IQIQIQ.....
83  // B-mode data: BBB..., BBB...
84  // => number of columns in the output image is half of the columns in the input image
85  int numberOfBmodeColumns = (inExt[1] - inExt[0] + 1) / 2;
86  outExt[0] = inExt[0] / 2;
87  outExt[1] = outExt[0] + numberOfBmodeColumns - 1;
88  }
89  break;
90  default:
91  vtkErrorMacro("Unknown RF image type: " << this->ImageType);
92  return 0;
93  }
94 
95  // Set the updated output image size
96  outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), outExt, 6);
97 
98  // Output is B-mode image, the pixel type is always unsigned 8-bit integer
99  vtkDataObject::SetPointDataActiveScalarInfo(outInfo, VTK_UNSIGNED_CHAR, -1);
100 
101  return 1;
102 }
103 
104 //----------------------------------------------------------------------------
106  vtkInformation* vtkNotUsed(request),
107  vtkInformationVector** inputVector,
108  vtkInformationVector* vtkNotUsed(outputVector),
109  vtkImageData*** inData,
110  vtkImageData** outData,
111  int outExt[6], int id)
112 {
113  if (outData[0]->GetScalarType() != VTK_UNSIGNED_CHAR)
114  {
115  vtkErrorMacro("Expecting VTK_UNSIGNED_CHAR output pixel type");
116  return;
117  }
118 
119  // Check input and output parameters
120  if (inData[0][0]->GetNumberOfScalarComponents() != 1)
121  {
122  vtkErrorMacro("Expecting 1 components not " << inData[0][0]->GetNumberOfScalarComponents());
123  return;
124  }
125  if (this->ImageType != US_IMG_BRIGHTNESS && inData[0][0]->GetScalarType() != VTK_SHORT && inData[0][0]->GetScalarType() != VTK_INT)
126  {
127  vtkErrorMacro("Expecting VTK_SHORT or VTK_INT as input pixel type, or US_IMG_BRIGHTNESS as image type");
128  return;
129  }
130 
131  int inExt[6] = {outExt[0], outExt[1], outExt[2], outExt[3], outExt[4], outExt[5]};
132  // Get the input extent for the output extent
133  switch (this->ImageType)
134  {
135  case US_IMG_BRIGHTNESS:
136  // => the output image size is the same as the input image size
137  // keep the default extent
138  break;
139  case US_IMG_RF_I_LINE_Q_LINE:
140  {
141  // RF data: IIIIII..., QQQQQQ....
142  // B-mode data: BBBBBB
143  // => number of rows in the output image is half of the rows in the input image
144  int numberOfBmodeRows = outExt[3] - outExt[2] + 1;
145  int numberOfRfRows = numberOfBmodeRows * 2;
146  inExt[2] = outExt[2] * 2;
147  inExt[3] = inExt[2] + numberOfRfRows - 1;
148  }
149  break;
150  case US_IMG_RF_REAL:
151  {
152  // RF data: III..., III...
153  // B-mode data: BBB..., BBB...
154  // => the output image size is the same as the input image size
155  // keep the default extent
156  }
157  break;
158  case US_IMG_RF_IQ_LINE:
159  {
160  // RF data: IQIQIQ....., IQIQIQ.....
161  // B-mode data: BBB..., BBB...
162  // => number of columns in the output image is half of the columns in the input image
163  int numberOfBmodeColumns = outExt[1] - outExt[0] + 1;
164  int numberOfRfColumns = numberOfBmodeColumns * 2;
165  inExt[0] = outExt[0] * 2;
166  inExt[1] = inExt[0] + numberOfRfColumns - 1;
167  }
168  break;
169  default:
170  vtkErrorMacro("Unknown RF image type: " << this->ImageType);
171  }
172 
173  if (inData[0][0]->GetScalarType() == VTK_SHORT)
174  {
175  ThreadedLineByLineHilbertTransform<short>(inExt, outExt, inData, outData, id);
176  }
177  else if (inData[0][0]->GetScalarType() == VTK_INT)
178  {
179  ThreadedLineByLineHilbertTransform<int>(inExt, outExt, inData, outData, id);
180  }
181  else if (inData[0][0]->GetScalarType() == VTK_UNSIGNED_CHAR)
182  {
183  ThreadedLineByLineHilbertTransform<unsigned char>(inExt, outExt, inData, outData, id);
184  }
185  else
186  {
187  vtkErrorMacro("Internal error: unexpected branching");
188  }
189 }
190 
191 template<typename ScalarType>
192 void vtkPlusRfToBrightnessConvert::ThreadedLineByLineHilbertTransform(int inExt[6], int outExt[6], vtkImageData*** inData, vtkImageData** outData, int threadId)
193 {
194  vtkIdType inInc0 = 0;
195  vtkIdType inInc1 = 0;
196  vtkIdType inInc2 = 0;
197  inData[0][0]->GetContinuousIncrements(inExt, inInc0, inInc1, inInc2);
198  ScalarType* inPtr = static_cast<ScalarType*>(inData[0][0]->GetScalarPointerForExtent(inExt));
199 
200  vtkIdType outInc0 = 0;
201  vtkIdType outInc1 = 0;
202  vtkIdType outInc2 = 0;
203  outData[0]->GetContinuousIncrements(outExt, outInc0, outInc1, outInc2);
204  unsigned char* outPtr = static_cast<unsigned char*>(outData[0]->GetScalarPointerForExtent(outExt));
205 
206  unsigned long target = static_cast<unsigned long>((outExt[5] - outExt[4] + 1) * (outExt[3] - outExt[2] + 1) / 50.0);
207  target++;
208 
209  // Temporary buffer to hold Hilbert transform results
210  int numberOfRfSamplesInScanline = inExt[1] - inExt[0] + 1;
211  int numberOfBmodeSamplesInScanline = outExt[1] - outExt[0] + 1;
212 
213  bool imageTypeValid = true;
214  unsigned long count = 0;
215  // loop over all the pixels (keeping track of normalized distance to origin.
216  if (this->ImageType == US_IMG_BRIGHTNESS)
217  {
218  // TODO: we copy the pixels, probably we could return the input image on the output in a more efficient manner
219  unsigned char* inPtrByte = static_cast<unsigned char*>(inData[0][0]->GetScalarPointerForExtent(inExt));
220  for (int idx2 = outExt[4]; idx2 <= outExt[5]; ++idx2)
221  {
222  for (int idx1 = outExt[2]; !this->AbortExecute && idx1 <= outExt[3]; ++idx1)
223  {
224  if (threadId == 0)
225  {
226  // it is the first thread, report progress
227  if (!(count % target))
228  {
229  this->UpdateProgress(count / (50.0 * target));
230  }
231  count++;
232  }
233  memcpy(outPtr, inPtrByte, numberOfRfSamplesInScanline + inInc1);
234  inPtrByte += numberOfRfSamplesInScanline + inInc1;
235  outPtr += numberOfRfSamplesInScanline + outInc1;
236  }
237  inPtrByte += inInc2;
238  outPtr += outInc2;
239  }
240  return;
241  }
242 
243  ScalarType* hilbertTransformBuffer = new ScalarType[numberOfRfSamplesInScanline + 1];
244  for (int idx2 = outExt[4]; idx2 <= outExt[5]; ++idx2)
245  {
246  for (int idx1 = outExt[2]; !this->AbortExecute && idx1 <= outExt[3]; ++idx1)
247  {
248  if (threadId == 0)
249  {
250  // it is the first thread, report progress
251  if (!(count % target))
252  {
253  this->UpdateProgress(count / (50.0 * target));
254  }
255  count++;
256  }
257 
258  switch (this->ImageType)
259  {
260  case US_IMG_RF_I_LINE_Q_LINE:
261  {
262  // e.g., Ultrasonix
263  // RF data: IIIIIII..., QQQQQQ...., IIIIIII..., QQQQQQ....
264  ScalarType* originalSignal = inPtr;
265  inPtr += numberOfRfSamplesInScanline + inInc1;
266  ScalarType* phaseShiftedSignal = inPtr;
267  inPtr += numberOfRfSamplesInScanline + inInc1;
268  ComputeAmplitudeILineQLine(outPtr, originalSignal, phaseShiftedSignal, numberOfRfSamplesInScanline);
269  outPtr += numberOfBmodeSamplesInScanline + outInc1;
270  }
271  break;
272  case US_IMG_RF_REAL:
273  {
274  // e.g., Ultrasonix
275  // RF data: IIIII..., IIIII...
276  ComputeHilbertTransform(hilbertTransformBuffer, inPtr, numberOfRfSamplesInScanline);
277  ComputeAmplitudeILineQLine(outPtr, inPtr, hilbertTransformBuffer, numberOfRfSamplesInScanline);
278  inPtr += numberOfRfSamplesInScanline + inInc1;
279  outPtr += numberOfBmodeSamplesInScanline + outInc1;
280  }
281  break;
282  case US_IMG_RF_IQ_LINE:
283  {
284  // RF data: IQIQIQ....., IQIQIQIQ.....
285  ComputeAmplitudeIqLine(outPtr, inPtr, numberOfRfSamplesInScanline);
286  inPtr += numberOfRfSamplesInScanline + inInc1;
287  outPtr += numberOfBmodeSamplesInScanline + outInc1;
288  }
289  break;
290  default:
291  imageTypeValid = false;
292  }
293  }
294  inPtr += inInc2;
295  outPtr += outInc2;
296  }
297  if (!imageTypeValid)
298  {
299  LOG_ERROR("Unsupported image type for brightness conversion: "<< igsioCommon::GetStringFromUsImageType(this->ImageType));
300  }
301 
302  delete[] hilbertTransformBuffer;
303  hilbertTransformBuffer = NULL;
304 }
305 
306 void vtkPlusRfToBrightnessConvert::PrintSelf(ostream& os, vtkIndent indent)
307 {
308  this->Superclass::PrintSelf(os, indent);
309 }
310 
311 //-----------------------------------------------------------------------------
312 PlusStatus vtkPlusRfToBrightnessConvert::ReadConfiguration(vtkXMLDataElement* rfToBrightnessElement)
313 {
314  LOG_TRACE("vtkPlusRfToBrightnessConvert::ReadConfiguration");
315  XML_VERIFY_ELEMENT(rfToBrightnessElement, "RfToBrightnessConversion");
316  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, NumberOfHilbertFilterCoeffs, rfToBrightnessElement);
317  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, BrightnessScale, rfToBrightnessElement);
318  return PLUS_SUCCESS;
319 }
320 
321 //-----------------------------------------------------------------------------
322 PlusStatus vtkPlusRfToBrightnessConvert::WriteConfiguration(vtkXMLDataElement* rfToBrightnessElement)
323 {
324  LOG_TRACE("vtkPlusRfToBrightnessConvert::WriteConfiguration");
325 
326  XML_VERIFY_ELEMENT(rfToBrightnessElement, "RfToBrightnessConversion");
327 
328  rfToBrightnessElement->SetDoubleAttribute("NumberOfHilbertFilterCoeffs", this->NumberOfHilbertFilterCoeffs);
329  rfToBrightnessElement->SetDoubleAttribute("BrightnessScale", this->BrightnessScale);
330 
331  return PLUS_SUCCESS;
332 }
333 
334 //-----------------------------------------------------------------------------
336 {
337  if ((int)(this->HilbertTransformCoeffs.size()) == this->NumberOfHilbertFilterCoeffs + 1)
338  {
339  // already computed the requested number of Hilbert transform coefficients
340  return;
341  }
342 
343  this->HilbertTransformCoeffs.resize(this->NumberOfHilbertFilterCoeffs + 1);
344  for (int i = 1; i <= this->NumberOfHilbertFilterCoeffs; i++)
345  {
346  // From http://www.vbforums.com/archive/index.php/t-639223.html
347  this->HilbertTransformCoeffs[i] = 1 / ((i - this->NumberOfHilbertFilterCoeffs / 2) - 0.5) / vtkMath::Pi();
348  }
349 
350  bool debugOutput = false; // print Hilbert transform coefficients in Matlab format
351  if (debugOutput)
352  {
353  std::cout << "hc = [ ";
354  for (int i = 1; i <= this->NumberOfHilbertFilterCoeffs; i++)
355  {
356  std::cout << this->HilbertTransformCoeffs[i];
357  if (i < this->NumberOfHilbertFilterCoeffs)
358  {
359  std::cout << ";";
360  }
361  if (i % 6 == 0)
362  {
363  std::cout << "\n";
364  }
365  }
366  std::cout << "]\n";
367  }
368 }
369 
370 template<typename ScalarType>
371 PlusStatus vtkPlusRfToBrightnessConvert::ComputeHilbertTransform(ScalarType* hilbertTransformOutput, ScalarType* input, int npt)
372 {
373  ComputeHilbertTransformCoeffs(); // update the transform coefficients if needed
374 
375  if (npt < this->NumberOfHilbertFilterCoeffs)
376  {
377  LOG_ERROR("Insufficient data for performing Hilbert transform");
378  return PLUS_FAIL;
379  }
380 
381  // Compute Hilbert transform by convolution
382  for (int l = 1; l <= npt - this->NumberOfHilbertFilterCoeffs + 1; l++)
383  {
384  double yt = 0.0;
385  for (int i = 1; i <= this->NumberOfHilbertFilterCoeffs; i++)
386  {
387  yt += input[l + i - 1] * this->HilbertTransformCoeffs[this->NumberOfHilbertFilterCoeffs + 1 - i];
388  }
389  hilbertTransformOutput[l] = yt;
390  }
391 
392  // Shift this->NumberOfHilbertFilterCoeffs/1+1/2 points
393  for (int i = 1; i <= npt - this->NumberOfHilbertFilterCoeffs; i++)
394  {
395  hilbertTransformOutput[i] = 0.5 * (hilbertTransformOutput[i] + hilbertTransformOutput[i + 1]);
396  }
397  for (int i = npt - this->NumberOfHilbertFilterCoeffs; i >= 1; i--)
398  {
399  hilbertTransformOutput[i + this->NumberOfHilbertFilterCoeffs / 2] = hilbertTransformOutput[i];
400  }
401 
402  // Pad by zeros
403  for (int i = 1; i <= this->NumberOfHilbertFilterCoeffs / 2; i++)
404  {
405  hilbertTransformOutput[i] = 0.0;
406  hilbertTransformOutput[npt + 1 - i] = 0.0;
407  }
408 
409  return PLUS_SUCCESS;
410 }
411 
412 template<typename ScalarType>
413 void vtkPlusRfToBrightnessConvert::ComputeAmplitudeILineQLine(unsigned char* ampl, ScalarType* inputSignal, ScalarType* inputSignalHilbertTransformed, int npt)
414 {
415  for (int i = 0; i < this->NumberOfHilbertFilterCoeffs / 2 + 1; i++)
416  {
417  ampl[i] = 0;
418  }
419  for (int i = this->NumberOfHilbertFilterCoeffs / 2 + 1; i <= npt - this->NumberOfHilbertFilterCoeffs / 2; i++)
420  {
421  double xt = inputSignal[i];
422  double xht = inputSignalHilbertTransformed[i];
423  double brightnessValue = sqrt(sqrt(sqrt(xt * xt + xht * xht))) * this->BrightnessScale;
424  if (brightnessValue > MAX_BRIGHTNESS_VALUE) { brightnessValue = MAX_BRIGHTNESS_VALUE; }
425  if (brightnessValue < MIN_BRIGHTNESS_VALUE) { brightnessValue = MIN_BRIGHTNESS_VALUE; }
426  ampl[i] = brightnessValue;
427  /*
428  If needed, the phase could be computed as follows:
429  phase[i] = atan2(xht ,xt);
430  omega[i] = phase[i]-phase[i-1];
431  if (omega[i]<0)
432  {
433  omega[i]+=2*pi;
434  }
435  */
436  }
437  for (int i = npt - this->NumberOfHilbertFilterCoeffs / 2 + 1; i < npt; i++)
438  {
439  ampl[i] = 0;
440  }
441 }
442 
443 template<typename ScalarType>
444 void vtkPlusRfToBrightnessConvert::ComputeAmplitudeIqLine(unsigned char* ampl, ScalarType* inputSignal, const int npt)
445 {
446  int inputIndex = 0;
447  int outputIndex = 0;
448  int numberOfIqPairs = floor(double(npt) / 2);
449  for (int i = 0; i < numberOfIqPairs; i++)
450  {
451  double xt = inputSignal[inputIndex++];
452  double xht = inputSignal[inputIndex++];
453  double outputValue = sqrt(sqrt(sqrt(xt * xt + xht * xht))) * this->BrightnessScale;
454  if (outputValue > MAX_BRIGHTNESS_VALUE) { outputValue = MAX_BRIGHTNESS_VALUE; }
455  if (outputValue < MIN_BRIGHTNESS_VALUE) { outputValue = MIN_BRIGHTNESS_VALUE; }
456  ampl[outputIndex++] = outputValue;
457  }
458 }
This class converts ultrasound RF data to brightness values.
void ThreadedRequestData(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector, vtkImageData ***inData, vtkImageData **outData, int outExt[6], int id)
void ThreadedLineByLineHilbertTransform(int inExt[6], int outExt[6], vtkImageData ***inData, vtkImageData **outData, int threadId)
void ComputeAmplitudeILineQLine(unsigned char *ampl, ScalarType *inputSignal, ScalarType *inputSignalHilbertTransformed, int npt)
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *rfToBrightnessElement)
igsioStatus PlusStatus
Definition: PlusCommon.h:40
for i
#define PLUS_FAIL
Definition: PlusCommon.h:43
vtkStandardNewMacro(vtkPlusRfToBrightnessConvert)
void ComputeAmplitudeIqLine(unsigned char *ampl, ScalarType *inputSignal, const int npt)
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
virtual int RequestInformation(vtkInformation *, vtkInformationVector **, vtkInformationVector *outputVector)
virtual PlusStatus WriteConfiguration(vtkXMLDataElement *rfToBrightnessElement)
Phidget_ChannelClass uint32_t * count
Definition: phidget22.h:1321
PlusStatus ComputeHilbertTransform(ScalarType *hilbertTransformOutput, ScalarType *input, int npt)
const double MAX_BRIGHTNESS_VALUE
const double MIN_BRIGHTNESS_VALUE
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE