PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
PlusSpatialModel.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 
9 #include "PlusSpatialModel.h"
10 
11 #include "vtkMath.h"
12 #include "vtkMatrix4x4.h"
13 #include "vtkModifiedBSPTree.h"
14 #include "vtkObjectFactory.h"
15 #include "vtkSTLReader.h"
16 #include "vtkXMLPolyDataReader.h"
17 #include "vtkPolyDataNormals.h"
18 #include "vtkProbeFilter.h"
19 #include "vtkPointData.h"
20 #include "vtkIdList.h"
21 #include "vtkTriangle.h"
22 
23 // If fraction of the transmitted beam intensity is smaller then this value then we consider the beam to be completely absorbed
24 const double MINIMUM_BEAM_INTENSITY = 1e-9;
25 
26 // Characterizes the specular reflection BRDF. If the value is smaller then reflection is limited to a smaller angle range (closer to 90deg incidence angle).
28 
29 //-----------------------------------------------------------------------------
31  : Name("")
32  , ModelFile("")
33  , ModelFileNeedsUpdate(false)
34  , ModelToObjectTransform(vtkMatrix4x4::New())
35  , ReferenceToObjectTransform(vtkMatrix4x4::New())
36  , ObjectCoordinateFrame("")
37  , ImagingFrequencyMhz(5.0)
38  , DensityKgPerM3(910)
39  , SoundVelocityMPerSec(1540)
40  , AttenuationCoefficientDbPerCmMhz(0.65)
41  , SurfaceReflectionIntensityDecayDbPerMm(20)
42  , BackscatterDiffuseReflectionCoefficient(0.1)
43  , TransducerSpatialModelMaxOverlapMm(10.0)
44  , SurfaceSpecularReflectionCoefficient(0.0)
45  , SurfaceDiffuseReflectionCoefficient(0.1)
46  , ModelLocalizer(vtkModifiedBSPTree::New())
47  , PolyData(NULL)
48 {
49 }
50 
51 //-----------------------------------------------------------------------------
53 {
54  SetModelToObjectTransform(static_cast<vtkMatrix4x4*>(NULL));
56  SetModelLocalizer(NULL);
57  SetPolyData(NULL);
58 }
59 
60 //-----------------------------------------------------------------------------
62 {
63  this->Name = model.Name;
65  this->ModelFile = model.ModelFile;
67  this->DensityKgPerM3 = model.DensityKgPerM3;
74  this->ModelToObjectTransform = NULL;
75  this->ReferenceToObjectTransform = NULL;
76  this->ModelLocalizer = NULL;
77  this->PolyData = NULL;
81  SetPolyData(model.PolyData);
85 }
86 
87 //-----------------------------------------------------------------------------
89 {
90  this->Name = model.Name;
92  this->ModelFile = model.ModelFile;
94  this->DensityKgPerM3 = model.DensityKgPerM3;
104  SetPolyData(model.PolyData);
108 }
109 
110 //-----------------------------------------------------------------------------
111 void PlusSpatialModel::SetModelToObjectTransform(vtkMatrix4x4* modelToObjectTransform)
112 {
113  if (this->ModelToObjectTransform == modelToObjectTransform)
114  {
115  return;
116  }
117  if (this->ModelToObjectTransform != NULL)
118  {
119  this->ModelToObjectTransform->Delete();
120  }
121  this->ModelToObjectTransform = modelToObjectTransform;
122  if (this->ModelToObjectTransform != NULL)
123  {
124  this->ModelToObjectTransform->Register(NULL);
125  }
126 }
127 
128 //-----------------------------------------------------------------------------
129 void PlusSpatialModel::SetReferenceToObjectTransform(vtkMatrix4x4* referenceToObjectTransform)
130 {
131  if (this->ReferenceToObjectTransform == referenceToObjectTransform)
132  {
133  return;
134  }
135  if (this->ReferenceToObjectTransform != NULL)
136  {
137  this->ReferenceToObjectTransform->Delete();
138  }
139  this->ReferenceToObjectTransform = referenceToObjectTransform;
140  if (this->ReferenceToObjectTransform != NULL)
141  {
142  this->ReferenceToObjectTransform->Register(NULL);
143  }
144 }
145 
146 //-----------------------------------------------------------------------------
147 void PlusSpatialModel::SetPolyData(vtkPolyData* polyData)
148 {
149  if (this->PolyData == polyData)
150  {
151  return;
152  }
153  if (this->PolyData != NULL)
154  {
155  this->PolyData->Delete();
156  }
157  this->PolyData = polyData;
158  if (this->PolyData != NULL)
159  {
160  this->PolyData->Register(NULL);
161  }
162 }
163 
164 //-----------------------------------------------------------------------------
165 void PlusSpatialModel::SetModelLocalizer(vtkModifiedBSPTree* modelLocalizer)
166 {
167  if (this->ModelLocalizer == modelLocalizer)
168  {
169  return;
170  }
171  if (this->ModelLocalizer != NULL)
172  {
173  this->ModelLocalizer->Delete();
174  }
175  this->ModelLocalizer = modelLocalizer;
176  if (this->ModelLocalizer != NULL)
177  {
178  this->ModelLocalizer->Register(NULL);
179  }
180 }
181 
182 //-----------------------------------------------------------------------------
183 PlusStatus PlusSpatialModel::ReadConfiguration(vtkXMLDataElement* spatialModelElement)
184 {
185  XML_VERIFY_ELEMENT(spatialModelElement, "SpatialModel");
186 
187  XML_READ_STRING_ATTRIBUTE_OPTIONAL(Name, spatialModelElement);
188  XML_READ_STRING_ATTRIBUTE_OPTIONAL(ObjectCoordinateFrame, spatialModelElement);
189  XML_READ_STRING_ATTRIBUTE_OPTIONAL(ModelFile, spatialModelElement);
190  XML_READ_VECTOR_ATTRIBUTE_OPTIONAL(double, 16, ModelToObjectTransform, spatialModelElement);
191  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, DensityKgPerM3, spatialModelElement);
192  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, SoundVelocityMPerSec, spatialModelElement);
193  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, AttenuationCoefficientDbPerCmMhz, spatialModelElement);
194  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, SurfaceReflectionIntensityDecayDbPerMm, spatialModelElement);
195  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, BackscatterDiffuseReflectionCoefficient, spatialModelElement);
196  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, SurfaceDiffuseReflectionCoefficient, spatialModelElement);
197  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, SurfaceSpecularReflectionCoefficient, spatialModelElement);
198  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, TransducerSpatialModelMaxOverlapMm, spatialModelElement);
199 
200  return PLUS_SUCCESS;
201 }
202 
203 //-----------------------------------------------------------------------------
205 {
206  double acousticImpedanceRayls = this->DensityKgPerM3 * this->SoundVelocityMPerSec; // kg / (s * m2)
207  return acousticImpedanceRayls * 1e-6; // megarayls
208 }
209 
210 //-----------------------------------------------------------------------------
211 void PlusSpatialModel::CalculateIntensity(std::vector<double>& reflectedIntensity, unsigned int numberOfFilledPixels, double distanceBetweenScanlineSamplePointsMm, double previousModelAcousticImpedanceMegarayls, double incidentIntensity, double& transmittedIntensity, double incidenceAngleRad)
212 {
213  UpdateModelFile();
214 
215  if (numberOfFilledPixels <= 0)
216  {
217  transmittedIntensity = incidentIntensity;
218  return;
219  }
220 
221  // Make sure there is enough space to store the results
222  if (reflectedIntensity.size() < numberOfFilledPixels)
223  {
224  reflectedIntensity.resize(numberOfFilledPixels);
225  }
226 
227  // Compute reflection from the surface of the previous and this model
228  double acousticImpedanceMegarayls = GetAcousticImpedanceMegarayls();
229  // intensityReflectionCoefficient: reflected beam intensity / incident beam intensity => can be computed from acoustic impedance mismatch
230  double intensityReflectionCoefficient =
231  (previousModelAcousticImpedanceMegarayls - acousticImpedanceMegarayls) * (previousModelAcousticImpedanceMegarayls - acousticImpedanceMegarayls)
232  / (previousModelAcousticImpedanceMegarayls + acousticImpedanceMegarayls) / (previousModelAcousticImpedanceMegarayls + acousticImpedanceMegarayls);
233  double surfaceReflectedBeamIntensity = incidentIntensity * intensityReflectionCoefficient;
234  double surfaceTransmittedBeamIntensity = incidentIntensity - surfaceReflectedBeamIntensity;
235 
236  // backscatteredReflectedIntensity: intensity reflected from the surface
237  double backscatteredReflectedIntensity = this->SurfaceDiffuseReflectionCoefficient * surfaceReflectedBeamIntensity;;
239  {
240  // There is specular reflection
241  // Normalize incidence angle to -90..90 deg
242  if (incidenceAngleRad > vtkMath::Pi() / 2)
243  {
244  incidenceAngleRad -= vtkMath::Pi();
245  }
246  else if (incidenceAngleRad < -vtkMath::Pi() / 2)
247  {
248  incidenceAngleRad += vtkMath::Pi();
249  }
250  const double stdev = vtkMath::RadiansFromDegrees(SPECULAR_REFLECTION_BRDF_STDEV);
251  float reflectionDirectionFactor = exp(-(incidenceAngleRad * incidenceAngleRad) / (stdev * stdev));
252  backscatteredReflectedIntensity += reflectionDirectionFactor * this->SurfaceSpecularReflectionCoefficient * surfaceReflectedBeamIntensity;
253  }
254 
255  // Compute attenuation within this model
256  double intensityAttenuationCoefficientdBPerPixel = this->AttenuationCoefficientDbPerCmMhz * (distanceBetweenScanlineSamplePointsMm / 10.0) * this->ImagingFrequencyMhz;
257  // intensityAttenuationCoefficientPerPixel: should be close to 1, as it's the ratio of (transmitted beam intensity / incident beam intensity) after traversing through a single pixel
258  double intensityAttenuationCoefficientPerPixel = pow(10.0, -intensityAttenuationCoefficientdBPerPixel / 10.0);
259  // intensityAttenuatedFractionPerPixel: how big fraction of the intensity is attenuated during traversing through one voxel
260  double intensityAttenuatedFractionPerPixel = (1 - intensityAttenuationCoefficientPerPixel);
261  // intensityTransmittedFractionPerPixelTwoWay: how big fraction of the intensity is transmitted during traversing through one voxel; takes into account both propagation directions
262  double intensityTransmittedFractionPerPixelTwoWay = intensityAttenuationCoefficientPerPixel * intensityAttenuationCoefficientPerPixel;
263 
264  transmittedIntensity = surfaceTransmittedBeamIntensity * intensityTransmittedFractionPerPixelTwoWay;
265 
266  if ((numberOfFilledPixels > 0 && this->PrecomputedAttenuations.size() < numberOfFilledPixels) || intensityTransmittedFractionPerPixelTwoWay != this->PrecomputedAttenuations[0])
267  {
268  UpdatePrecomputedAttenuations(intensityTransmittedFractionPerPixelTwoWay, numberOfFilledPixels);
269  }
270 
271  // We iterate until transmittedIntensity * intensityTransmittedFractionPerPixelTwoWay^n > MINIMUM_BEAM_INTENSITY
272  // So, n = log(MINIMUM_BEAM_INTENSITY/transmittedIntensity) / log(intensityTransmittedFractionPerPixelTwoWay)
273  unsigned int numberOfIterationsToReachMinimumBeamIntensity = 0;
274  if (transmittedIntensity > MINIMUM_BEAM_INTENSITY)
275  {
276  numberOfIterationsToReachMinimumBeamIntensity =
277  std::min<unsigned int>(numberOfFilledPixels, // value may be larger than number of pixels to fill -> clamp it to the number of pixels to fill
278  static_cast<unsigned int>(std::max<int>(0, // value may be negative when AttenuationCoefficientDbPerCmMhz is close to 0 -> clamp it to zero
279  floor(log(MINIMUM_BEAM_INTENSITY / transmittedIntensity) / log(intensityTransmittedFractionPerPixelTwoWay)) + 1)));
280  double backScatterFactor = transmittedIntensity * intensityAttenuatedFractionPerPixel * this->BackscatterDiffuseReflectionCoefficient / intensityTransmittedFractionPerPixelTwoWay;
281  for (unsigned int currentPixelInFilledPixels = 0; currentPixelInFilledPixels < numberOfIterationsToReachMinimumBeamIntensity; currentPixelInFilledPixels++)
282  {
283  // a fraction of the attenuation is caused by backscattering, the backscattering is sensed by the transducer
284  //reflectedIntensity[currentPixelInFilledPixels] = *(attenuation++) * backScatterFactor;
285  reflectedIntensity[currentPixelInFilledPixels] = this->PrecomputedAttenuations[currentPixelInFilledPixels] * backScatterFactor;
286  }
287  transmittedIntensity *= pow(intensityTransmittedFractionPerPixelTwoWay, static_cast<double>(numberOfIterationsToReachMinimumBeamIntensity));
288  }
289  else
290  {
291  transmittedIntensity = 0;
292  }
293  // The beam intensity is very close to 0, so fill the remaining values with 0 instead of computing miniscule values
294  for (unsigned int currentPixelInFilledPixels = numberOfIterationsToReachMinimumBeamIntensity; currentPixelInFilledPixels < numberOfFilledPixels; currentPixelInFilledPixels++)
295  {
296  reflectedIntensity[currentPixelInFilledPixels] = 0.0;
297  }
298 
299  // Add surface reflection
300  if (backscatteredReflectedIntensity > MINIMUM_BEAM_INTENSITY)
301  {
302  double surfaceReflectionIntensityDecayPerPixel = pow(10.0, -this->SurfaceReflectionIntensityDecayDbPerMm * distanceBetweenScanlineSamplePointsMm / 10.0);
303  // We iterate until backscatteredReflectedIntensity * surfaceReflectionIntensityDecayPerPixel^n > MINIMUM_BEAM_INTENSITY
304  // So, n = log(MINIMUM_BEAM_INTENSITY/backscatteredReflectedIntensity) / log(surfaceReflectionIntensityDecayPerPixel)
305  int numberOfIterationsToReachMinimumBackscatteredIntensity = std::min<int>(numberOfFilledPixels, floor(log(MINIMUM_BEAM_INTENSITY / backscatteredReflectedIntensity) / log(surfaceReflectionIntensityDecayPerPixel)) + 1);
306  for (int currentPixelInFilledPixels = 0; currentPixelInFilledPixels < numberOfIterationsToReachMinimumBackscatteredIntensity; currentPixelInFilledPixels++)
307  {
308  // a fraction of the attenuation is caused by backscattering, the backscattering is sensed by the transducer
309  reflectedIntensity[currentPixelInFilledPixels] += backscatteredReflectedIntensity;
310  backscatteredReflectedIntensity *= surfaceReflectionIntensityDecayPerPixel;
311  }
312  }
313  // TODO: to simulate beamwidth, take into account the incidence angle and disperse the reflection on a larger area if the angle is large
314 }
315 
316 //-----------------------------------------------------------------------------
317 void PlusSpatialModel::GetLineIntersections(std::deque<LineIntersectionInfo>& lineIntersections, double* scanLineStartPoint_Reference, double* scanLineEndPoint_Reference)
318 {
319  UpdateModelFile();
320 
321  if (this->ModelFile.empty())
322  {
323  // no model is defined, which means that the model is everywhere
324  // add an intersection point at 0 distance, which means that the whole scanline is in this model
325  LineIntersectionInfo intersectionInfo;
326  intersectionInfo.Model = this;
327  intersectionInfo.IntersectionIncidenceAngleRad = 0;
328  intersectionInfo.IntersectionDistanceFromStartPointMm = 0;
329  lineIntersections.push_back(intersectionInfo);
330  return;
331  }
332 
333  // non-normalized direction vector of the scanline
334  double scanLineDirectionVector_Reference[4] =
335  {
336  scanLineEndPoint_Reference[0] - scanLineStartPoint_Reference[0],
337  scanLineEndPoint_Reference[1] - scanLineStartPoint_Reference[1],
338  scanLineEndPoint_Reference[2] - scanLineStartPoint_Reference[2],
339  0
340  };
341  double scanLineDirectionVectorNorm_Reference = vtkMath::Norm(scanLineDirectionVector_Reference);
342  double searchLineStartPoint_Reference[4] = {0, 0, 0, 1};
343  for (int i = 0; i < 3; i++)
344  {
345  searchLineStartPoint_Reference[i] = scanLineStartPoint_Reference[i] - this->TransducerSpatialModelMaxOverlapMm * scanLineDirectionVector_Reference[i] / scanLineDirectionVectorNorm_Reference;
346  }
347 
348  vtkSmartPointer<vtkMatrix4x4> objectToModelMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
349  vtkMatrix4x4::Invert(this->ModelToObjectTransform, objectToModelMatrix);
350  vtkSmartPointer<vtkMatrix4x4> referenceToModelMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
351  vtkMatrix4x4::Multiply4x4(objectToModelMatrix, this->ReferenceToObjectTransform, referenceToModelMatrix);
352 
353  double searchLineStartPoint_Model[4] = {0, 0, 0, 1};
354  double scanLineEndPoint_Model[4] = {0, 0, 0, 1};
355  referenceToModelMatrix->MultiplyPoint(searchLineStartPoint_Reference, searchLineStartPoint_Model);
356  referenceToModelMatrix->MultiplyPoint(scanLineEndPoint_Reference, scanLineEndPoint_Model);
357 
358  vtkSmartPointer<vtkPoints> intersectionPoints_Model = vtkSmartPointer<vtkPoints>::New();
359  vtkSmartPointer<vtkIdList> intersectionCellIds = vtkSmartPointer<vtkIdList>::New();
360  this->ModelLocalizer->IntersectWithLine(searchLineStartPoint_Model, scanLineEndPoint_Model, 0.0, intersectionPoints_Model, intersectionCellIds);
361 
362  if (intersectionPoints_Model->GetNumberOfPoints() < 1)
363  {
364  // no intersections with this model
365  return;
366  }
367 
368  vtkSmartPointer<vtkMatrix4x4> modelToReferenceMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
369  vtkMatrix4x4::Invert(referenceToModelMatrix, modelToReferenceMatrix);
370 
371  // Measure the distance from the starting point in the reference coordinate system
372  double intersectionPoint_Model[4] = {0, 0, 0, 1};
373  double intersectionPoint_Reference[4] = {0, 0, 0, 1};
374  int intersectionPointIndex = 0;
375  bool scanLineStartPointInsideModel = false;
376  // Search for intersection points in the search line that are not part of the scanline to detect
377  // potential model/transducer overlap
378  for (; intersectionPointIndex < intersectionPoints_Model->GetNumberOfPoints(); intersectionPointIndex++)
379  {
380  intersectionPoints_Model->GetPoint(intersectionPointIndex, intersectionPoint_Model);
381  modelToReferenceMatrix->MultiplyPoint(intersectionPoint_Model, intersectionPoint_Reference);
382  double intersectionDistanceFromSearchLineStartPointMm = sqrt(vtkMath::Distance2BetweenPoints(searchLineStartPoint_Reference, intersectionPoint_Reference));
383  if (intersectionDistanceFromSearchLineStartPointMm <= this->TransducerSpatialModelMaxOverlapMm)
384  {
385  // there is an intersection point in the search line that is not part of the scanline
386  scanLineStartPointInsideModel = (!scanLineStartPointInsideModel);
387  }
388  else
389  {
390  // we reached the scanline starting point
391  break;
392  }
393  }
394  LineIntersectionInfo intersectionInfo;
395  intersectionInfo.Model = this;
396  if (scanLineStartPointInsideModel)
397  {
398  // the scanline starting point is inside the model, so add an intersection point at 0 distance
399  intersectionInfo.IntersectionDistanceFromStartPointMm = 0;
400  lineIntersections.push_back(intersectionInfo);
401  }
402 
403  // Get surface normals at intersection points
404  vtkDataArray* normals_Model = NULL;
405  if (this->PolyData->GetPointData())
406  {
407  normals_Model = this->PolyData->GetPointData()->GetNormals();
408  }
409 
410  double scanLineDirectionVector_Model[4] = {0, 0, 0, 0};
411  referenceToModelMatrix->MultiplyPoint(scanLineDirectionVector_Reference, scanLineDirectionVector_Model);
412  vtkMath::Normalize(scanLineDirectionVector_Model);
413 
414  for (; intersectionPointIndex < intersectionPoints_Model->GetNumberOfPoints(); intersectionPointIndex++)
415  {
416  intersectionPoints_Model->GetPoint(intersectionPointIndex, intersectionPoint_Model);
417  modelToReferenceMatrix->MultiplyPoint(intersectionPoint_Model, intersectionPoint_Reference);
418  intersectionInfo.IntersectionDistanceFromStartPointMm = sqrt(vtkMath::Distance2BetweenPoints(scanLineStartPoint_Reference, intersectionPoint_Reference));
419  vtkTriangle* cell = vtkTriangle::SafeDownCast(this->PolyData->GetCell(intersectionCellIds->GetId(intersectionPointIndex)));
420  if (cell != NULL && normals_Model != NULL)
421  {
422  const int NUMBER_OF_POINTS_PER_CELL = 3; // triangle cell
423  double pcoords[NUMBER_OF_POINTS_PER_CELL] = {0, 0, 0};
424  double dist2 = 0;
425  double weights[NUMBER_OF_POINTS_PER_CELL] = {0, 0, 0};
426  double closestPoint[3] = {0, 0, 0};
427  int subId = 0;
428  cell->EvaluatePosition(intersectionPoint_Model, closestPoint, subId, pcoords, dist2, weights);
429  double interpolatedNormal_Model[3] = {0, 0, 0};
430  for (int pointIndex = 0; pointIndex < NUMBER_OF_POINTS_PER_CELL; pointIndex++)
431  {
432  double* normalAtCellCorner = normals_Model->GetTuple3(cell->GetPointId(pointIndex));
433  if (normalAtCellCorner == NULL)
434  {
435  LOG_ERROR("SpatialModel::GetLineIntersections error: invalid normal");
436  continue;
437  }
438  interpolatedNormal_Model[0] += normalAtCellCorner[0] * weights[pointIndex];
439  interpolatedNormal_Model[1] += normalAtCellCorner[1] * weights[pointIndex];
440  interpolatedNormal_Model[2] += normalAtCellCorner[2] * weights[pointIndex];
441  }
442  vtkMath::Normalize(interpolatedNormal_Model);
443  intersectionInfo.IntersectionIncidenceAngleRad = acos(vtkMath::Dot(interpolatedNormal_Model, scanLineDirectionVector_Model));
444  }
445  else
446  {
447  LOG_ERROR("SpatialModel::GetLineIntersections error: surface normal is not available");
448  intersectionInfo.IntersectionIncidenceAngleRad = 0;
449  }
450  lineIntersections.push_back(intersectionInfo);
451  }
452 }
453 
454 //-----------------------------------------------------------------------------
456 {
457  if (!this->ModelFileNeedsUpdate)
458  {
459  return PLUS_SUCCESS;
460  }
461 
462  this->ModelFileNeedsUpdate = false;
463 
464  if (this->PolyData != NULL)
465  {
466  this->PolyData->Delete();
467  this->PolyData = NULL;
468  }
469 
470  if (this->ModelFile.empty())
471  {
472  LOG_DEBUG("ModelFileName is not specified for SpatialModel " << (this->Name.empty() ? "(undefined)" : this->Name) << " it will be used as background media");
473  return PLUS_SUCCESS;
474  }
475 
476  std::string foundAbsoluteImagePath;
477  // FindImagePath is used instead of FindModelPath, as the model is expected to be in the image directory
478  // it might be more reasonable to move the model to the model directory and change this to FindModelPath
479  if (vtkPlusConfig::GetInstance()->FindImagePath(this->ModelFile, foundAbsoluteImagePath) != PLUS_SUCCESS)
480  {
481  LOG_ERROR("Cannot find input model file " << this->ModelFile);
482  return PLUS_FAIL;
483  }
484 
485  vtkSmartPointer<vtkPolyData> polyData;
486 
487  std::string fileExt = vtksys::SystemTools::GetFilenameLastExtension(foundAbsoluteImagePath);
488  if (igsioCommon::IsEqualInsensitive(fileExt, ".stl"))
489  {
490  vtkSmartPointer<vtkSTLReader> modelReader = vtkSmartPointer<vtkSTLReader>::New();
491  modelReader->SetFileName(foundAbsoluteImagePath.c_str());
492  modelReader->Update();
493  polyData = modelReader->GetOutput();
494  }
495  else //if (igsioCommon::IsEqualInsensitive(fileExt.c_str(),".vtp"))
496  {
497  vtkSmartPointer<vtkXMLPolyDataReader> modelReader = vtkSmartPointer<vtkXMLPolyDataReader>::New();
498  modelReader->SetFileName(foundAbsoluteImagePath.c_str());
499  modelReader->Update();
500  polyData = modelReader->GetOutput();
501  }
502 
503  if (polyData.GetPointer() == NULL || polyData->GetNumberOfPoints() == 0)
504  {
505  LOG_ERROR("Model specified cannot be found: " << foundAbsoluteImagePath);
506  return PLUS_FAIL;
507  }
508 
509  vtkSmartPointer<vtkPolyDataNormals> polyDataNormalsComputer = vtkSmartPointer<vtkPolyDataNormals>::New();
510  polyDataNormalsComputer->SetInputData(polyData);
511  polyDataNormalsComputer->Update();
512  this->PolyData = polyDataNormalsComputer->GetOutput();
513  this->PolyData->Register(NULL);
514 
515  this->ModelLocalizer->SetDataSet(this->PolyData);
516  this->ModelLocalizer->SetMaxLevel(24);
517  this->ModelLocalizer->SetNumberOfCellsPerNode(32);
518  this->ModelLocalizer->BuildLocator();
519 
520  return PLUS_SUCCESS;
521 }
522 
523 //-----------------------------------------------------------------------------
524 void PlusSpatialModel::SetModelFile(const std::string& modelFile)
525 {
526  if (this->ModelFile.compare(modelFile) != 0)
527  {
528  this->ModelFileNeedsUpdate = true;
529  }
530  this->ModelFile = modelFile;
531 }
532 
533 //-----------------------------------------------------------------------------
534 void PlusSpatialModel::UpdatePrecomputedAttenuations(double intensityTransmittedFractionPerPixelTwoWay, int numberOfElements)
535 {
536  this->PrecomputedAttenuations.resize(numberOfElements);
537  double attenuation = intensityTransmittedFractionPerPixelTwoWay;
538  for (int i = 0; i < numberOfElements; i++)
539  {
540  this->PrecomputedAttenuations[i] = attenuation;
541  attenuation *= intensityTransmittedFractionPerPixelTwoWay;
542  }
543 }
544 
545 //-----------------------------------------------------------------------------
547 {
548  this->ModelToObjectTransform->DeepCopy(matrixElements);
549 }
vtkMatrix4x4 * ReferenceToObjectTransform
void SetModelLocalizer(vtkModifiedBSPTree *modelLocalizer)
std::string ModelFile
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *spatialModelElement)
void SetReferenceToObjectTransform(vtkMatrix4x4 *referenceToObjectTransform)
igsioStatus PlusStatus
Definition: PlusCommon.h:40
vtkMatrix4x4 * ModelToObjectTransform
for i
double SurfaceReflectionIntensityDecayDbPerMm
#define PLUS_FAIL
Definition: PlusCommon.h:43
const double MINIMUM_BEAM_INTENSITY
static vtkPlusConfig * GetInstance()
double BackscatterDiffuseReflectionCoefficient
double AttenuationCoefficientDbPerCmMhz
void SetModelFile(const std::string &modelFile)
double SurfaceSpecularReflectionCoefficient
void CalculateIntensity(std::vector< double > &reflectedIntensity, unsigned int numberOfFilledPixels, double distanceBetweenScanlineSamplePointsMm, double previousModelAcousticImpedanceMegarayls, double incidentIntensity, double &transmittedIntensity, double incidenceAngleRad)
std::string ObjectCoordinateFrame
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
double SPECULAR_REFLECTION_BRDF_STDEV
void UpdatePrecomputedAttenuations(double intensityTransmittedFractionPerPixelTwoWay, int numberOfElements)
void SetPolyData(vtkPolyData *polyData)
vtkPolyData * PolyData
double SurfaceDiffuseReflectionCoefficient
void operator=(const PlusSpatialModel &model)
double TransducerSpatialModelMaxOverlapMm
PlusStatus UpdateModelFile()
vtkModifiedBSPTree * ModelLocalizer
virtual ~PlusSpatialModel()
std::vector< double > PrecomputedAttenuations
void GetLineIntersections(std::deque< LineIntersectionInfo > &lineIntersections, double *scanLineStartPoint_Reference, double *scanLineEndPoint_Reference)
void SetModelToObjectTransform(vtkMatrix4x4 *modelToObjectTransform)
double GetAcousticImpedanceMegarayls()