CustusX  20.03-rc1
An IGT application
cxUSFrameData.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
12 
13 #include "cxUSFrameData.h"
14 
15 #include <vtkImageData.h>
16 #include <vtkImageLuminance.h>
17 #include <vtkImageClip.h>
18 #include <vtkImageAppend.h>
19 #include <vtkMetaImageWriter.h>
20 #include <vtkImageImport.h>
21 #include "cxTypeConversions.h"
22 #include <QFileInfo>
23 #include "cxTimeKeeper.h"
24 #include "cxImageDataContainer.h"
25 #include "cxVolumeHelpers.h"
26 #include "cxLogger.h"
27 #include "cxFileManagerService.h"
28 #include "cxImage.h"
29 
30 
31 typedef vtkSmartPointer<vtkImageAppend> vtkImageAppendPtr;
32 
33 namespace cx
34 {
35 
36 ProcessedUSInputData::ProcessedUSInputData(std::vector<vtkImageDataPtr> frames, std::vector<TimedPosition> pos, vtkImageDataPtr mask, QString path, QString uid) :
37  mProcessedImage(frames),
38  mFrames(pos),
39  mMask(mask),
40  mPath(path),
41  mUid(uid)
42 {
43  this->validate();
44 }
45 
47 {
48  std::vector<TimedPosition> frameInfo = this->getFrames();
49  Eigen::Array3i inputDims = this->getDimensions();
50 
51  if (inputDims[2] != static_cast<int>(frameInfo.size()))
52  {
53  QString msg("Mismatch in US input data: inputDims[2] != frameInfo.size() : %1 != %2");
54  reportWarning(msg.arg(inputDims[2]).arg(frameInfo.size()));
55  return false;
56  }
57 
58  if (inputDims[2]==0)
59  {
60  reportWarning("Empty US input data");
61  return false;
62  }
63 
64  return true;
65 }
66 
67 unsigned char* ProcessedUSInputData::getFrame(unsigned int index) const
68 {
69  CX_ASSERT(index < mProcessedImage.size());
70 
71  // Raw data pointer
72  unsigned char *inputPointer = static_cast<unsigned char*> (mProcessedImage[index]->GetScalarPointer());
73  return inputPointer;
74 }
75 
77 {
78  Eigen::Array3i retval;
79  retval[0] = mProcessedImage[0]->GetDimensions()[0];
80  retval[1] = mProcessedImage[0]->GetDimensions()[1];
81  retval[2] = mProcessedImage.size();
82  return retval;
83 }
84 
86 {
87  Vector3D retval = Vector3D(mProcessedImage[0]->GetSpacing());
88  retval[2] = retval[0]; // set z-spacing to arbitrary value.
89  return retval;
90 }
91 
92 std::vector<TimedPosition> ProcessedUSInputData::getFrames() const
93 {
94  return mFrames;
95 }
96 
98 {
99  return mMask;
100 }
101 
103 {
104  return mPath;
105 }
106 
108 {
109  return mUid;
110 }
111 
112 
116 
118 {
119  USFrameDataPtr retval(new USFrameData());
120 
121  retval->mName = QFileInfo(inputFrameData->getName()).completeBaseName();
122  retval->mImageContainer.reset(new cx::SplitFramesContainer(inputFrameData->getBaseVtkImageData()));
123  retval->resetRemovedFrames();
124 
125  return retval;
126 }
127 
134 USFrameDataPtr USFrameData::create(QString inputFilename, FileManagerServicePtr fileManager)
135 {
136  QFileInfo info(inputFilename);
137 
138  TimeKeeper timer;
139  QString mhdSingleFile = info.absolutePath()+"/"+info.completeBaseName()+".mhd";
140 
141  if (QFileInfo(mhdSingleFile).exists())
142  {
143  vtkImageDataPtr image = fileManager->loadVtkImageData(mhdSingleFile);
144  // load from single file
145  USFrameDataPtr retval = USFrameData::create(ImagePtr(new Image(mhdSingleFile, image)));
146  retval->mName = QFileInfo(mhdSingleFile).completeBaseName();
147  timer.printElapsedms(QString("Loading single %1").arg(inputFilename));
148  return retval;
149  }
150  else
151  {
152  USFrameDataPtr retval(new USFrameData());
153  retval->mName = QFileInfo(inputFilename).completeBaseName();
154  retval->mImageContainer.reset(new cx::CachedImageDataContainer(inputFilename, -1, fileManager));
155  retval->resetRemovedFrames();
156  return retval;
157  }
158 }
159 
161 {
162  if (!images)
163  return USFrameDataPtr();
164  USFrameDataPtr retval(new USFrameData());
165  retval->mName = name;
166  retval->mImageContainer = images;
167  retval->resetRemovedFrames();
168 
169  return retval;
170 }
171 
172 USFrameDataPtr USFrameData::create(QString name, std::vector<vtkImageDataPtr> frames)
173 {
174  cx::ImageDataContainerPtr container;
175  container.reset(new cx::FramesDataContainer(frames));
176  return create(name, container);
177 }
178 
179 
181  mCropbox(0,0,0,0,0,0), mPurgeInput(true)
182 {
183 }
184 
186 {
187  if (mImageContainer->empty())
188  return;
189 
190  mReducedToFull.clear();
191  for (unsigned i=0; i<mImageContainer->size(); ++i)
192  mReducedToFull.push_back(i);
193 }
194 
196 {
197  mImageContainer.reset();
198 }
199 
203 void USFrameData::removeFrame(unsigned int index)
204 {
205  CX_ASSERT(index < mReducedToFull.size());
206  mReducedToFull.erase(mReducedToFull.begin()+index);
207 }
208 
209 Eigen::Array3i USFrameData::getDimensions() const
210 {
211  vtkImageDataPtr image = mImageContainer->get(0);
212  Eigen::Array3i retval(image->GetDimensions());
213 
214  if (mCropbox.range()[0]!=0)
215  {
216  // WARNING: cannot use dimensions from the cropImage function - it uses extent,
217  // which may be larger. We need the real size, given by wholeextent/updateextent.
218  vtkImageDataPtr sample = this->cropImageExtent(mImageContainer->get(0), mCropbox);
219 // Eigen::Array<int, 6, 1> extent(sample->GetWholeExtent());
220  Eigen::Array<int, 6, 1> extent(sample->GetExtent());
221  retval[0] = extent[1]-extent[0]+1;
222  retval[1] = extent[3]-extent[2]+1;
223 // std::cout << "extent dim: " << retval[0] << " " << retval[1] << std::endl;
224  }
225 
226  retval[2] = mReducedToFull.size();
227  return retval;
228 }
229 
230 
232 {
233  Vector3D retval(1,1,1);
234  if (!mImageContainer->empty())
235  retval = Vector3D(mImageContainer->get(0)->GetSpacing());
236  retval[2] = retval[0]; // set z-spacing to arbitrary value.
237  return retval;
238 }
239 
241 {
242  // ensure clip never happens in z dir.
243  cropbox[4] = -100000;
244  cropbox[5] = 100000;
245  mCropbox = cropbox;
246 }
247 
255 {
256  vtkImageClipPtr clip = vtkImageClipPtr::New();
257  clip->SetInputData(input);
258  clip->SetOutputWholeExtent(cropbox.begin());
259 // clip->ClipDataOn(); // this line sets wholeextent==extent, but uses lots of time (~6ms/frame)
260  clip->Update();
261  vtkImageDataPtr rawResult = clip->GetOutput();
262 // rawResult->Update();
263  rawResult->Crop(cropbox.begin()); // moved in from toGrayscaleAndEffectuateCropping when VTK5->6
264  return rawResult;
265 }
266 
272 {
273  vtkImageDataPtr grayScaleData;
274 
275  if (input->GetNumberOfScalarComponents() == 1) // already gray
276  {
277  // crop (slow)
278  grayScaleData = input;
279 // outData->Crop();
280  }
281  else
282  {
283  // convert and crop as side effect (optimization)
284  grayScaleData = convertImageDataToGrayScale(input);
285  }
286 
287  vtkImageDataPtr outData = this->convertTo8bit(grayScaleData);
288 
289  vtkImageDataPtr copy = vtkImageDataPtr::New();
290  copy->DeepCopy(outData);
291  return copy;
292 }
293 
294 vtkImageDataPtr USFrameData::convertTo8bit(vtkImageDataPtr input) const
295 {
296  vtkImageDataPtr retval = input;
297  if (input->GetScalarSize() > 1)
298  {
299  ImagePtr tempImage = cx::ImagePtr(new cx::Image("tempImage", input, "tempImage"));
300  tempImage->resetTransferFunctions();
301  retval = tempImage->get8bitGrayScaleVtkImageData();
302  }
303  return retval;
304 }
305 
306 // for testing
307 //void printStuff(QString text, vtkImageDataPtr data)
308 //{
309 // std::cout << text << std::endl;
310 // Eigen::Array<int, 6, 1> wholeExtent(data->GetWholeExtent());
311 // Eigen::Array<int, 6, 1> extent(data->GetExtent());
312 // Eigen::Array<int, 6, 1> updateExtent(data->GetUpdateExtent());
313 // std::cout << "\t whole ext " << wholeExtent << std::endl;
314 // std::cout << "\tupdate ext " << updateExtent << std::endl;
315 // std::cout << "\t ext " << extent << std::endl;
317 
318 // Eigen::Array<vtkIdType,3,1> hinc;
319 // data->GetContinuousIncrements(data->GetWholeExtent(), hinc[0], hinc[1], hinc[2]);
320 // std::cout << "\t whole inc " << hinc << std::endl;
321 
322 // Eigen::Array<vtkIdType,3,1> uinc;
323 // data->GetContinuousIncrements(data->GetUpdateExtent(), uinc[0], uinc[1], uinc[2]);
324 // std::cout << "\t uinc " << uinc << std::endl;
325 
326 // Eigen::Array<vtkIdType,3,1> inc;
327 // data->GetContinuousIncrements(data->GetExtent(), inc[0], inc[1], inc[2]);
328 // std::cout << "\t inc " << inc << std::endl;
329 
330 // Eigen::Array3i dim(data->GetDimensions());
331 // std::cout << "\t dim " << dim << std::endl;
332 
333 // Eigen::Array3i max(wholeExtent[1] - wholeExtent[0], wholeExtent[3] - wholeExtent[2], wholeExtent[5] - wholeExtent[4]);
334 // std::cout << "\t max " << max << std::endl;
335 //}
336 
338 {
339  // Some of the code here is borrowed from the vtk examples:
340  // http://public.kitware.com/cgi-bin/viewcvs.cgi/*checkout*/Examples/Build/vtkMy/Imaging/vtkImageFoo.cxx?root=VTK&content-type=text/plain
341 
342  if (inData->GetNumberOfScalarComponents() != 3)
343  {
344  if(frameNum == 0) //Only report warning once
345  reportWarning("Angio requested for grayscale ultrasound");
346  return grayFrame;
347  }
348 
349  vtkImageDataPtr outData = vtkImageDataPtr::New();
350  outData->DeepCopy(grayFrame);
351 // outData->Update(); // updates whole extent.
352 
353 // printStuff("Clipped color in", inData);
354 // printStuff("Grayscale in", outData);
355 
356 // int* inExt = inData->GetWholeExtent();
357  int* outExt = outData->GetExtent();
358 
359  // Remember that the input might (and do due to vtkImageClip) contain leaps.
360  // This means that the wholeextent might be larger than the extent, thus
361  // we must use a startoffset and leaps between lines.
362 
363  unsigned char *inPtr = static_cast<unsigned char*> (inData->GetScalarPointerForExtent(inData->GetExtent()));
364  unsigned char *outPtr = static_cast<unsigned char*> (outData->GetScalarPointerForExtent(outData->GetExtent()));
365 
366  int maxX = outExt[1] - outExt[0];
367  int maxY = outExt[3] - outExt[2];
368  int maxZ = outExt[5] - outExt[4];
369 
370  Eigen::Array<vtkIdType,3,1> inInc;
371  inData->GetContinuousIncrements(inData->GetExtent(), inInc[0], inInc[1], inInc[2]);
372  CX_ASSERT(inInc[0]==0);
373  // we assume (wholeextent == extent) for the outData in the algorithm below. assert here.
374  Eigen::Array<vtkIdType,3,1> outInc;
375  outData->GetContinuousIncrements(outData->GetExtent(), outInc[0], outInc[1], outInc[2]);
376  CX_ASSERT(outInc[0]==0);
377  CX_ASSERT(outInc[1]==0);
378  CX_ASSERT(outInc[2]==0);
379 
380  for (int z=0; z<=maxZ; z++)
381  {
382  for (int y=0; y<=maxY; y++)
383  {
384  for (int x=0; x<=maxX; x++)
385  {
386  //Look at 3 scalar components at the same time (RGB),
387  //if color is grayscale or close to grayscale: set to zero.
388 
389  if (((*inPtr) == (*(inPtr + 1))) && ((*inPtr) == (*(inPtr + 2))))
390  {
391  (*outPtr) = 0;
392  }
393  else
394  {
395  // strong check: look for near-gray values and remove them.
396  double r = inPtr[0];
397  double g = inPtr[1];
398  double b = inPtr[2];
399  int metric = (fabs(r-g) + fabs(r-b) + fabs(g-b)) / 3; // average absolute diff must be less than or equal to this
400 // std::cout << QString(" %1,%2,%3 \t %4, %5 -- %6").arg(int(inPtr[0])).arg(int(inPtr[1])).arg(int(inPtr[2])).arg(idxR).arg(idxY).arg(metric) << std::endl;
401  if (metric <= 3)
402  (*outPtr) = 0;
403 
404  }
405  //Assume the outVolume is treated with the luminance filter first
406  outPtr++;
407  inPtr += 3;
408  }
409  inPtr += inInc[1];
410  }
411  inPtr += inInc[2];
412  }
413 
414  return outData;
415 }
416 
418 {
419  mPurgeInput = value;
420 }
421 
422 std::vector<std::vector<vtkImageDataPtr> > USFrameData::initializeFrames(std::vector<bool> angio)
423 {
424 
425  std::vector<std::vector<vtkImageDataPtr> > raw(angio.size());
426 
427  for (unsigned i=0; i<raw.size(); ++i)
428  {
429  raw[i].resize(mReducedToFull.size());
430  }
431 
432  // apply cropping and angio
433  for (unsigned i=0; i<mReducedToFull.size(); ++i)
434  {
437 
438  if (mCropbox.range()[0]!=0)
439  current = this->cropImageExtent(current, mCropbox);
440 
441  // optimization: grayFrame is used in both calculations: compute once
442  vtkImageDataPtr grayFrame = this->to8bitGrayscaleAndEffectuateCropping(current);
443 
444  for (unsigned j=0; j<angio.size(); ++j)
445  {
446 
447  if (angio[j])
448  {
449  vtkImageDataPtr angioFrame = this->useAngio(current, grayFrame, i);
450  raw[j][i] = angioFrame;
451  }
452  else
453  {
454  raw[j][i] = grayFrame;
455  }
456  }
457 
458  if (mPurgeInput)
459  mImageContainer->purge(mReducedToFull[i]);
460  }
461 
462  if (mPurgeInput)
463  mImageContainer->purgeAll();
464 
465  return raw;
466 }
467 
469 {
470  mImageContainer->purgeAll();
471 }
472 
474 {
475  USFrameDataPtr retval(new USFrameData(*this));
476  this->resetRemovedFrames();
477  return retval;
478 }
479 
480 QString USFrameData::getName() const
481 {
482  return QFileInfo(mName).completeBaseName();
483 }
484 
486 {
487  return mImageContainer->size();
488 }
489 
491 {
492  TimeKeeper timer;
493  vtkImageDataPtr source = mImageContainer->get(index);
494 
495 // use this test code to display angio output:
496 // vtkImageDataPtr current = mImageContainer->get(index);
497 // current = this->cropImage(current, IntBoundingBox3D(157, 747, 68, 680, 0, 0));
498 // vtkImageDataPtr grayFrame = this->toGrayscale(current);
499 // static vtkImageDataPtr source;
500 // source = this->useAngio(current, grayFrame);
501 
502 // source->Update(); // VTK5->6
503 
504  import->SetImportVoidPointer(source->GetScalarPointer());
505  import->SetDataScalarType(source->GetScalarType());
506  import->SetDataSpacing(source->GetSpacing());
507  import->SetNumberOfScalarComponents(source->GetNumberOfScalarComponents());
508  import->SetWholeExtent(source->GetExtent());
509  import->SetDataExtentToWholeExtent();
510 }
511 
513 {
514  int numberOfFrames = mReducedToFull.size();
515 
516  if (numberOfFrames > 0)
517  {
518  vtkImageDataPtr image = mImageContainer->get(0);
519  if(image->GetDataDimension() == 3)
520  {
521  return true;
522  }
523 
524  }
525  return false;
526 }
527 
529 {
530  if (mImageContainer->get(0)->GetNumberOfScalarComponents() == 1)
531  return true;
532  return false;
533 }
534 
535 }//namespace cx
vtkImageDataPtr cropImageExtent(vtkImageDataPtr input, IntBoundingBox3D cropbox) const
Use only US angio data as input. Removes grayscale from the US data and converts the remaining color ...
boost::shared_ptr< class FileManagerService > FileManagerServicePtr
bool is8bit() const
unsigned char * getFrame(unsigned int index) const
vtkSmartPointer< vtkImageAppend > vtkImageAppendPtr
std::vector< std::vector< vtkImageDataPtr > > initializeFrames(std::vector< bool > angio)
cx::ImageDataContainerPtr mImageContainer
#define CX_ASSERT(statement)
Definition: cxLogger.h:116
void setPurgeInputDataAfterInitialize(bool value)
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:27
boost::shared_ptr< class USFrameData > USFrameDataPtr
void printElapsedms(QString text="Elapsed") const
std::vector< int > mReducedToFull
map from indexes in the reduced volume to the full (original) volume.
Eigen::Array3i getDimensions() const
IntBoundingBox3D mCropbox
unsigned getNumImages()
QString getName() const
std::vector< TimedPosition > getFrames() const
Helper class encapsulating a 2S US data set.
Definition: cxUSFrameData.h:87
virtual USFrameDataPtr copy()
Eigen::Vector3i range() const
void reportWarning(QString msg)
Definition: cxLogger.cpp:70
vtkImageDataPtr getMask()
Eigen::Array3i getDimensions() const
A volumetric data set.
Definition: cxImage.h:45
vtkImageDataPtr useAngio(vtkImageDataPtr inData, vtkImageDataPtr grayFrame, int frameNum) const
void fillImageImport(vtkImageImportPtr import, int index)
fill import with a single frame
Representation of an integer bounding box in 3D. The data are stored as {xmin,xmax,ymin,ymax,zmin,zmax}, in order to simplify communication with vtk.
vtkSmartPointer< class vtkImageClip > vtkImageClipPtr
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:42
vtkImageDataPtr convertImageDataToGrayScale(vtkImageDataPtr image)
vtkSmartPointer< class vtkImageImport > vtkImageImportPtr
Vector3D getSpacing() const
void setCropBox(IntBoundingBox3D mCropbox)
Vector3D getSpacing() const
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
vtkImageDataPtr to8bitGrayscaleAndEffectuateCropping(vtkImageDataPtr input) const
ProcessedUSInputData(std::vector< vtkImageDataPtr > frames, std::vector< TimedPosition > pos, vtkImageDataPtr mask, QString path, QString uid)
static USFrameDataPtr create(ImagePtr inputFrameData)
void removeFrame(unsigned int index)
boost::shared_ptr< class ImageDataContainer > ImageDataContainerPtr
Definition: cxUSFrameData.h:24
Namespace for all CustusX production code.