PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
DrawClipRegion.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 // Local includes
8 #include "PlusConfigure.h"
9 #include "igsioCommon.h"
10 #include "PlusMath.h"
11 #include "igsioTrackedFrame.h"
12 #include "igsioVideoFrame.h"
13 #include "vtkPlusSequenceIO.h"
14 #include "vtkIGSIOTrackedFrameList.h"
16 
17 // VTK includes
18 #include <vtkImageData.h>
19 #include <vtkMath.h>
20 #include <vtkSmartPointer.h>
21 #include <vtkXMLUtilities.h>
22 #include <vtksys/CommandLineArguments.hxx>
23 
24 namespace
25 {
26  static const float DRAWING_COLOR = 255;
27 }
28 
29 //----------------------------------------------------------------------------
30 void DrawSector(vtkImageData* imageData, int* imageExtent, double* origin, double* fanAnglesDeg, double radius, int numberOfPoints)
31 {
32  double startAngleRad = vtkMath::RadiansFromDegrees(fanAnglesDeg[0]);
33  double deltaAngleRad = vtkMath::RadiansFromDegrees((fanAnglesDeg[1] - fanAnglesDeg[0]) / (numberOfPoints - 1));
34  for (int pointIndex = 0; pointIndex < numberOfPoints; ++pointIndex)
35  {
36  double angleRad = startAngleRad + pointIndex * deltaAngleRad;
37  int pixelCoordX = origin[0] + radius * sin(angleRad);
38  int pixelCoordY = origin[1] + radius * cos(angleRad);
39  if (pixelCoordX < imageExtent[0] || pixelCoordX > imageExtent[1] || pixelCoordY < imageExtent[2] || pixelCoordY > imageExtent[3])
40  {
41  // outside of the specified extent
42  continue;
43  }
44  imageData->SetScalarComponentFromFloat(pixelCoordX, pixelCoordY, 0, 0, DRAWING_COLOR);
45  }
46 }
47 
48 //----------------------------------------------------------------------------
49 void DrawFan(vtkImageData* imageData, double* fanOrigin, double startRadius, double stopRadius, double* fanAnglesDeg, int pointSpacing, bool drawOrigin)
50 {
51  double fanAnglesRad[2] = {vtkMath::RadiansFromDegrees(fanAnglesDeg[0]), vtkMath::RadiansFromDegrees(fanAnglesDeg[1])};
52  int* extent = imageData->GetExtent();
53  // origin to start radius (to highlight the origin in the image)
54  if (drawOrigin)
55  {
56  int numberOfPointsForDrawing = startRadius / 10; // 1/10 point/pixel: dotted line
57  double rounded = std::round(fanOrigin[1]);
58  unsigned int val = static_cast<unsigned int>(rounded);
59  std::array<int, 3> startPoint = { static_cast<int>(std::round(fanOrigin[0])),
60  static_cast<int>(std::round(fanOrigin[1])),
61  0
62  };
63  std::array<int, 3> endPointLeft = { static_cast<int>(std::round(fanOrigin[0] + startRadius * sin(fanAnglesRad[0]))),
64  static_cast<int>(std::round(fanOrigin[1] + startRadius * cos(fanAnglesRad[0]))),
65  0
66  };
67  std::array<int, 3> endPointRight = { static_cast<int>(std::round(fanOrigin[0] + startRadius * sin(fanAnglesRad[1]))),
68  static_cast<int>(std::round(fanOrigin[1] + startRadius * cos(fanAnglesRad[1]))),
69  0
70  };
71  igsioCommon::DrawLine(*imageData, DRAWING_COLOR, igsioCommon::LINE_STYLE_DOTS, startPoint, endPointLeft, numberOfPointsForDrawing);
72  igsioCommon::DrawLine(*imageData, DRAWING_COLOR, igsioCommon::LINE_STYLE_DOTS, startPoint, endPointRight, numberOfPointsForDrawing);
73  }
74  // side lines from start to stop radius
75  {
76  int numberOfPointsForDrawing = (stopRadius - startRadius) / pointSpacing;
77  {
78  std::array<int, 3> startPoint = { static_cast<int>(std::round(fanOrigin[0] + startRadius * sin(fanAnglesRad[0]))),
79  static_cast<int>(std::round(fanOrigin[1] + startRadius * cos(fanAnglesRad[0]))),
80  0
81  };
82  std::array<int, 3> endPoint = { static_cast<int>(std::round(fanOrigin[0] + stopRadius * sin(fanAnglesRad[0]))),
83  static_cast<int>(std::round(fanOrigin[1] + stopRadius * cos(fanAnglesRad[0]))),
84  0
85  };
86  igsioCommon::DrawLine(*imageData, DRAWING_COLOR, igsioCommon::LINE_STYLE_DOTS, startPoint, endPoint, numberOfPointsForDrawing);
87  }
88 
89  {
90  std::array<int, 3> startPoint = { static_cast<int>(std::round(fanOrigin[0] + startRadius * sin(fanAnglesRad[1]))),
91  static_cast<int>(std::round(fanOrigin[1] + startRadius * cos(fanAnglesRad[1]))),
92  0
93  };
94  std::array<int, 3> endPoint = { static_cast<int>(std::round(fanOrigin[0] + stopRadius * sin(fanAnglesRad[1]))),
95  static_cast<int>(std::round(fanOrigin[1] + stopRadius * cos(fanAnglesRad[1]))),
96  0
97  };
98  igsioCommon::DrawLine(*imageData, DRAWING_COLOR, igsioCommon::LINE_STYLE_DOTS, startPoint, endPoint, numberOfPointsForDrawing);
99  }
100  }
101  // circle sectors at start and stop radius
102  {
103  int numberOfPointsForDrawing = startRadius * (fanAnglesRad[1] - fanAnglesRad[0]) / pointSpacing; // sector length
104  DrawSector(imageData, extent, fanOrigin, fanAnglesDeg, startRadius, numberOfPointsForDrawing);
105  numberOfPointsForDrawing = stopRadius * (fanAnglesRad[1] - fanAnglesRad[0]) / pointSpacing; // sector length
106  DrawSector(imageData, extent, fanOrigin, fanAnglesDeg, stopRadius, numberOfPointsForDrawing);
107  }
108 }
109 
110 //----------------------------------------------------------------------------
111 void DrawClipRectangle(vtkImageData* imageData, vtkPlusVolumeReconstructor* reconstructor)
112 {
113  int* clipRectangleOrigin = reconstructor->GetClipRectangleOrigin();
114  int* clipRectangleSize = reconstructor->GetClipRectangleSize();
115  int* extent = imageData->GetExtent();
116  int numberOfPoints = 200; // number of drawn points per line
117 
118  // Horizontal lines
119  {
120  std::array<int, 3> startPoint = { static_cast<int>(std::round(clipRectangleOrigin[0])),
121  static_cast<int>(std::round(clipRectangleOrigin[1])),
122  0
123  };
124  std::array<int, 3> endPoint = { static_cast<int>(std::round(clipRectangleOrigin[0] + clipRectangleSize[0] - 1)),
125  static_cast<int>(std::round(clipRectangleOrigin[1])),
126  0
127  };
128  igsioCommon::DrawLine(*imageData, DRAWING_COLOR, igsioCommon::LINE_STYLE_DOTS, startPoint, endPoint, numberOfPoints);
129  }
130  {
131  std::array<int, 3> startPoint = { static_cast<int>(std::round(clipRectangleOrigin[0])),
132  static_cast<int>(std::round(clipRectangleOrigin[1] + clipRectangleSize[1] - 1)),
133  0
134  };
135  std::array<int, 3> endPoint = { static_cast<int>(std::round(clipRectangleOrigin[0] + clipRectangleSize[0] - 1)),
136  static_cast<int>(std::round(clipRectangleOrigin[1] + clipRectangleSize[1] - 1)),
137  0
138  };
139  igsioCommon::DrawLine(*imageData, DRAWING_COLOR, igsioCommon::LINE_STYLE_DOTS, startPoint, endPoint, numberOfPoints);
140  }
141 
142  // Vertical lines
143  {
144  std::array<int, 3> startPoint = { static_cast<int>(std::round(clipRectangleOrigin[0])),
145  static_cast<int>(std::round(clipRectangleOrigin[1])),
146  0
147  };
148  std::array<int, 3> endPoint = { static_cast<int>(std::round(clipRectangleOrigin[0])),
149  static_cast<int>(std::round(clipRectangleOrigin[1] + clipRectangleSize[1] - 1)),
150  0
151  };
152  igsioCommon::DrawLine(*imageData, DRAWING_COLOR, igsioCommon::LINE_STYLE_DOTS, startPoint, endPoint, numberOfPoints);
153  }
154  {
155  std::array<int, 3> startPoint = { static_cast<int>(std::round(clipRectangleOrigin[0] + clipRectangleSize[0] - 1)),
156  static_cast<int>(std::round(clipRectangleOrigin[1])),
157  0
158  };
159  std::array<int, 3> endPoint = { static_cast<int>(std::round(clipRectangleOrigin[0] + clipRectangleSize[0] - 1)),
160  static_cast<int>(std::round(clipRectangleOrigin[1] + clipRectangleSize[1] - 1)),
161  0
162  };
163  igsioCommon::DrawLine(*imageData, DRAWING_COLOR, igsioCommon::LINE_STYLE_DOTS, startPoint, endPoint, numberOfPoints);
164  }
165 }
166 
167 //----------------------------------------------------------------------------
168 void DrawClipFan(vtkImageData* imageData, vtkPlusVolumeReconstructor* reconstructor)
169 {
170  bool isImageEmpty = false;
171  reconstructor->UpdateFanAnglesFromImage(imageData, isImageEmpty);
172  if (!reconstructor->FanClippingApplied())
173  {
174  return;
175  }
176 
177  double* fanOrigin = reconstructor->GetFanOrigin();
178  double* maxFanAnglesDeg = reconstructor->GetFanAnglesDeg();
179  double* detectedFanAnglesDeg = reconstructor->GetDetectedFanAnglesDeg();
180  double fanRadiusStartPixel = reconstructor->GetFanRadiusStartPixel();
181  double fanRadiusStopPixel = reconstructor->GetFanRadiusStopPixel();
182 
183  if (isImageEmpty)
184  {
185  // draw maximum fan angles with dotted line
186  DrawFan(imageData, fanOrigin, fanRadiusStartPixel, fanRadiusStopPixel, maxFanAnglesDeg, 10, true /* draw origin*/);
187  }
188  else
189  {
190  if (reconstructor->GetEnableFanAnglesAutoDetect())
191  {
192  // draw maximum fan angles with dotted line
193  DrawFan(imageData, fanOrigin, fanRadiusStartPixel, fanRadiusStopPixel, maxFanAnglesDeg, 10, true /* draw origin*/);
194  DrawFan(imageData, fanOrigin, fanRadiusStartPixel, fanRadiusStopPixel, detectedFanAnglesDeg, 1, false /* do not draw origin*/);
195  }
196  else
197  {
198  // no fan angle auto-detect => max angle range is used => draw maximum fan angles with solid line
199  DrawFan(imageData, fanOrigin, fanRadiusStartPixel, fanRadiusStopPixel, maxFanAnglesDeg, 1, true /* draw origin*/);
200  }
201  }
202 }
203 
204 
205 //----------------------------------------------------------------------------
206 int main(int argc, char** argv)
207 {
208  // Setup for command line arguments
209  bool printHelp(false);
210  std::string inputImgSeqFileName;
211  std::string outputImgSeqFileName;
212  std::string inputConfigFileName;
213  int verboseLevel = vtkPlusLogger::LOG_LEVEL_UNDEFINED;
214 
215  vtksys::CommandLineArguments args;
216  args.Initialize(argc, argv);
217 
218  args.AddArgument("--source-seq-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputImgSeqFileName, "Input ultrasound image sequence.");
219  args.AddArgument("--output-seq-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &outputImgSeqFileName, "Output ultrasound sequence, with clipping rectangle and fan overlaid.");
220  args.AddArgument("--config-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputConfigFileName, "The ultrasound sequence config file.");
221  args.AddArgument("--help", vtksys::CommandLineArguments::NO_ARGUMENT, &printHelp, "Print this help.");
222  args.AddArgument("--verbose", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &verboseLevel, "Verbose level (1=error only, 2=warning, 3=info, 4=debug, 5=trace)");
223 
224  // Fail if arguments can't be parsed
225  if (!args.Parse())
226  {
227  std::cerr << "Error parsing arguments." << std::endl;
228  std::cout << "Help: " << args.GetHelp() << std::endl;
229  exit(EXIT_FAILURE);
230  }
231  // Print help if requested
232  if (printHelp)
233  {
234  std::cout << args.GetHelp() << std::endl;
235  exit(EXIT_SUCCESS);
236  }
237 
238  vtkPlusLogger::Instance()->SetLogLevel(verboseLevel);
239 
240  // Fail if no ultrasound image file specified
241  if (inputImgSeqFileName.empty())
242  {
243  LOG_ERROR("--seq-file required");
244  exit(EXIT_FAILURE);
245  }
246  // Fail if no ultrasound config file specified
247  if (inputConfigFileName.empty())
248  {
249  LOG_ERROR("--config-file required");
250  exit(EXIT_FAILURE);
251  }
252 
253  // Read the image sequence
254  vtkSmartPointer<vtkIGSIOTrackedFrameList> trackedFrameList = vtkSmartPointer<vtkIGSIOTrackedFrameList>::New();
255  if (vtkPlusSequenceIO::Read(inputImgSeqFileName, trackedFrameList) != PLUS_SUCCESS)
256  {
257  LOG_ERROR("Unable to load input sequences file.");
258  exit(EXIT_FAILURE);
259  }
260 
261  // For reading the configuration file
262  vtkSmartPointer<vtkXMLDataElement> configRootElement = vtkSmartPointer<vtkXMLDataElement>::New();
263  if (PlusXmlUtils::ReadDeviceSetConfigurationFromFile(configRootElement, inputConfigFileName.c_str()) == PLUS_FAIL)
264  {
265  LOG_ERROR("Unable to read configuration from file " << inputConfigFileName.c_str());
266  return EXIT_FAILURE;
267  }
268  vtkXMLDataElement* volumeReconstructionElement = configRootElement->LookupElementWithName("VolumeReconstruction");
269  if (volumeReconstructionElement == NULL)
270  {
271  LOG_ERROR("VolumeReconstruction element was not found in input configuration file");
272  return EXIT_FAILURE;
273  }
274  vtkSmartPointer<vtkPlusVolumeReconstructor> reconstructor = vtkSmartPointer<vtkPlusVolumeReconstructor>::New();
275  if (reconstructor->ReadConfiguration(volumeReconstructionElement->GetParent()) == PLUS_FAIL)
276  {
277  LOG_ERROR("Failed to parse VolumeReconstruction element in input configuration file");
278  return EXIT_FAILURE;
279  }
280 
281  // Draw
282  int numberOfFrames = trackedFrameList->GetNumberOfTrackedFrames();
283  LOG_INFO("Processing " << numberOfFrames << " frames...");
284  for (int frameIndex = 0; frameIndex < numberOfFrames; frameIndex++)
285  {
286  igsioTrackedFrame* frame = trackedFrameList->GetTrackedFrame(frameIndex);
287  vtkImageData* imageData = frame->GetImageData()->GetImage();
288  DrawClipRectangle(imageData, reconstructor);
289  DrawClipFan(imageData, reconstructor);
290  }
291 
292  // Write the new TrackedFrameList to metafile
293  LOG_INFO("Writing new sequence to file...");
294  if (outputImgSeqFileName.empty())
295  {
296  int extensionDot = inputImgSeqFileName.find_last_of(".");
297  if (extensionDot != std::string::npos)
298  {
299  inputImgSeqFileName = inputImgSeqFileName.substr(0, extensionDot);
300  }
301  outputImgSeqFileName = inputImgSeqFileName + "-Scanlines.nrrd";
302  }
303  if (vtkPlusSequenceIO::Write(outputImgSeqFileName, trackedFrameList) != PLUS_SUCCESS)
304  {
305  //Error has already been logged
306  return EXIT_FAILURE;
307  }
308  LOG_INFO("Writing to " << outputImgSeqFileName << " complete.");
309 
310  return EXIT_SUCCESS;
311 }
#define PLUS_FAIL
Definition: PlusCommon.h:43
int main(int argc, char **argv)
static igsioStatus Write(const std::string &filename, igsioTrackedFrame *frame, US_IMAGE_ORIENTATION orientationInFile=US_IMG_ORIENT_MF, bool useCompression=true, bool EnableImageDataWrite=true)
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
static igsioStatus Read(const std::string &filename, vtkIGSIOTrackedFrameList *frameList)
Reconstructs a volume from tracked frames.
void DrawSector(vtkImageData *imageData, int *imageExtent, double *origin, double *fanAnglesDeg, double radius, int numberOfPoints)
void DrawClipFan(vtkImageData *imageData, vtkPlusVolumeReconstructor *reconstructor)
void DrawClipRectangle(vtkImageData *imageData, vtkPlusVolumeReconstructor *reconstructor)
static vtkIGSIOLogger * Instance()
void DrawFan(vtkImageData *imageData, double *fanOrigin, double startRadius, double stopRadius, double *fanAnglesDeg, int pointSpacing, bool drawOrigin)
static PlusStatus ReadDeviceSetConfigurationFromFile(vtkXMLDataElement *config, const char *filename)
Definition: PlusXmlUtils.h:23