iMSTK
Interactive Medical Simulation Toolkit
imstkFastMarch.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include "imstkFastMarch.h"
8 #include "imstkLogger.h"
9 #include "imstkVecDataArray.h"
10 
11 namespace imstk
12 {
13 void
14 FastMarch::solve()
15 {
16  // Get the scalars (ensure they're single component doubles)
17  std::shared_ptr<AbstractDataArray> abstractScalars = m_imageData->getScalars();
18  if (abstractScalars->getScalarType() != IMSTK_DOUBLE || abstractScalars->getNumberOfComponents() != 1)
19  {
20  LOG(WARNING) << "fastMarch only works with single component double images";
21  return;
22  }
23  auto scalars = std::dynamic_pointer_cast<DataArray<double>>(abstractScalars);
24  double* imgPtr = scalars->getPointer();
25  m_dim = m_imageData->getDimensions();
26  m_spacing = m_imageData->getSpacing();
27  m_indexShift = m_dim[0] * m_dim[1];
28 
29  // We maintain a solution in maps, to keep things sparse
30 
31  // Sparse container for which nodes are marked visited
32  m_visited = std::unordered_set<int>();
33  // Sparse container for nodal distances
34  m_distances = std::unordered_map<int, double>(); // Solved distances
35 
36  m_queue = std::priority_queue<Node, std::vector<Node>, NodeComparator>();
37 
38  // Add the initial seeds to the queue
39  for (size_t i = 0; i < m_seedVoxels.size(); i++)
40  {
41  const Vec3i& coord = m_seedVoxels[i];
42  if (coord[0] < 0 || coord[0] >= m_dim[0]
43  || coord[1] < 0 || coord[1] >= m_dim[1]
44  || coord[2] < 0 || coord[2] >= m_dim[2])
45  {
46  continue;
47  }
48  const int index = static_cast<int>(m_imageData->getScalarIndex(coord));
49  m_distances[index] = imgPtr[m_imageData->getScalarIndex(coord)];
50  m_queue.push(Node(index, 0.0, coord));
51  }
52 
53  // Process every node in order of minimum distance
54  while (!m_queue.empty())
55  {
56  Node node = m_queue.top();
57  m_queue.pop();
58 
59  const Vec3i& coord = node.m_coord;
60  const int& nodeId = node.m_nodeId;
61 
62  // Termination conditions
63  if (isVisited(nodeId)
64  || getDistance(nodeId) >= m_distThreshold)
65  {
66  continue;
67  }
68 
69  // Mark node as visited (to avoid readdition)
70  m_visited.insert(nodeId);
71 
72  // Update all its neighbor cells (diagonals not considered neighbors)
73  // Right +x
74  int neighborId = nodeId + 1;
75  Vec3i neighborCoord = coord + Vec3i(1, 0, 0);
76  if (neighborCoord[0] < m_dim[0] && !isVisited(neighborId))
77  {
78  solveNode(neighborCoord, neighborId);
79  }
80  // Left -x
81  neighborId = nodeId - 1;
82  neighborCoord = coord - Vec3i(1, 0, 0);
83  if (neighborCoord[0] >= 0 && !isVisited(neighborId))
84  {
85  solveNode(neighborCoord, neighborId);
86  }
87 
88  // Up +y
89  neighborId = nodeId + m_dim[0];
90  neighborCoord = coord + Vec3i(0, 1, 0);
91  if (neighborCoord[1] < m_dim[1] && !isVisited(neighborId))
92  {
93  solveNode(neighborCoord, neighborId);
94  }
95  // Down -y
96  neighborId = nodeId - m_dim[0];
97  neighborCoord = coord - Vec3i(0, 1, 0);
98  if (neighborCoord[1] >= 0 && !isVisited(neighborId))
99  {
100  solveNode(neighborCoord, neighborId);
101  }
102 
103  // Forward +z
104  neighborId = nodeId + m_indexShift;
105  neighborCoord = coord + Vec3i(0, 0, 1);
106  if (neighborCoord[2] < m_dim[2] && !isVisited(neighborId))
107  {
108  solveNode(neighborCoord, neighborId);
109  }
110  // Backward -z
111  neighborId = nodeId - m_indexShift;
112  neighborCoord = coord - Vec3i(0, 0, 1);
113  if (neighborCoord[2] >= 0 && !isVisited(neighborId))
114  {
115  solveNode(neighborCoord, neighborId);
116  }
117  }
118 
119  // Write the sparse distances to the image
120  for (auto i : m_distances)
121  {
122  imgPtr[i.first] = i.second;
123  }
124 }
125 
126 void
127 FastMarch::solveNode(Vec3i coord, int index)
128 {
129  // Compute the min distance in each axes
130  const double dists[6] =
131  {
132  coord[0] - 1 >= 0 ? getDistance(index - 1) : IMSTK_DOUBLE_MAX,
133  coord[0] + 1 < m_dim[0] ? getDistance(index + 1) : IMSTK_DOUBLE_MAX,
134  coord[1] - 1 >= 0 ? getDistance(index - m_dim[0]) : IMSTK_DOUBLE_MAX,
135  coord[1] + 1 < m_dim[1] ? getDistance(index + m_dim[0]) : IMSTK_DOUBLE_MAX,
136  coord[2] - 1 >= 0 ? getDistance(index - m_indexShift) : IMSTK_DOUBLE_MAX,
137  coord[2] + 1 < m_dim[2] ? getDistance(index + m_indexShift) : IMSTK_DOUBLE_MAX
138  };
139  const double minDist[3] =
140  {
141  std::min(dists[0], dists[1]),
142  std::min(dists[2], dists[3]),
143  std::min(dists[4], dists[5])
144  };
145 
146  // Sort so that the min of minDist is first
147  int dimReorder[3] = { 0, 1, 2 };
148  for (int i = 0; i < 3; i++)
149  {
150  for (int j = i + 1; j < 3; j++)
151  {
152  const int dim1 = dimReorder[i];
153  const int dim2 = dimReorder[j];
154  if (minDist[dim1] > minDist[dim2])
155  {
156  std::swap(dimReorder[i], dimReorder[j]);
157  }
158  }
159  }
160 
161  double aa = 0.0;
162  double bb = 0.0;
163  double cc = -1.0;
164 
165  // For every dimension
166  double solution = IMSTK_DOUBLE_MAX;
167  double discrim = 0.0;
168  // todo: Sort dimensions by minDist, start with smallest (break faster)
169  for (unsigned int i = 0; i < 3; i++)
170  {
171  const double value = minDist[dimReorder[i]];
172  if (solution >= value)
173  {
174  const double spaceFactor = std::sqrt(1.0 / m_spacing[dimReorder[i]]);
175  aa += spaceFactor;
176  bb += value * spaceFactor;
177  cc += value * value * spaceFactor;
178 
179  discrim = bb * bb - aa * cc;
180  if (discrim < 0.0)
181  {
182  // Whoops
183  return;
184  }
185 
186  solution = (std::sqrt(discrim) + bb) / aa;
187  }
188  else
189  {
190  break;
191  }
192  }
193 
194  if (solution < IMSTK_DOUBLE_MAX)
195  {
196  // Accept it as the new distance
197  m_distances[index] = solution;
198  m_queue.push(Node(index, solution, coord));
199  }
200 }
201 } // namespace imstk
Compound Geometry.