fw4spl
ImagePositionPatientSplitter.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2009-2018.
3  * Distributed under the terms of the GNU Lesser General Public License (LGPL) as
4  * published by the Free Software Foundation.
5  * ****** END LICENSE BLOCK ****** */
6 
7 #include "fwDicomIOFilter/splitter/ImagePositionPatientSplitter.hpp"
8 
9 #include "fwDicomIOFilter/exceptions/FilterFailure.hpp"
10 #include "fwDicomIOFilter/registry/macros.hpp"
11 
12 #include <fwMath/VectorFunctions.hpp>
13 
14 #include <dcmtk/config/osconfig.h>
15 #include <dcmtk/dcmdata/dcdeftag.h>
16 #include <dcmtk/dcmdata/dcfilefo.h>
17 #include <dcmtk/dcmdata/dcistrmb.h>
18 #include <dcmtk/dcmimgle/dcmimage.h>
19 #include <dcmtk/dcmnet/diutil.h>
20 
21 fwDicomIOFilterRegisterMacro( ::fwDicomIOFilter::splitter::ImagePositionPatientSplitter );
22 
23 namespace fwDicomIOFilter
24 {
25 namespace splitter
26 {
27 
28 const std::string ImagePositionPatientSplitter::s_FILTER_NAME = "Image position patient splitter";
30  "Split instances by finding gaps in image position continuity. This filter assume that "
31  "the instances are <b>already sorted</b> and only gaps between volumes remain.";
32 
33 //-----------------------------------------------------------------------------
34 
36  ISplitter()
37 {
38 }
39 
40 //-----------------------------------------------------------------------------
41 
43 {
44 }
45 
46 //-----------------------------------------------------------------------------
47 
49 {
51 }
52 
53 //-----------------------------------------------------------------------------
54 
56 {
58 }
59 
60 //-----------------------------------------------------------------------------
61 
62 ImagePositionPatientSplitter::DicomSeriesContainerType ImagePositionPatientSplitter::apply(
63  const ::fwMedData::DicomSeries::sptr& series, const ::fwLog::Logger::sptr& logger)
64 const
65 {
66  DicomSeriesContainerType result;
67 
68  OFCondition status;
69  DcmDataset* dataset;
70 
71  double previousIndex = 0.;
72  unsigned int instanceNumber = 0;
73  double spacingBetweenSlices = 0.;
74  const double epsilon = 1e-2; // Value used to find a gap
75  ::fwMedData::DicomSeries::sptr currentSeries;
76  for(const auto& item : series->getDicomContainer())
77  {
78  const ::fwMemory::BufferObject::sptr bufferObj = item.second;
79  const size_t buffSize = bufferObj->getSize();
80  ::fwMemory::BufferObject::Lock lock(bufferObj);
81  char* buffer = static_cast< char* >( lock.getBuffer() );
82 
83  DcmInputBufferStream is;
84  is.setBuffer(buffer, offile_off_t(buffSize));
85  is.setEos();
86 
87  DcmFileFormat fileFormat;
88  fileFormat.transferInit();
89  if (!fileFormat.read(is).good())
90  {
91  FW_RAISE("Unable to read Dicom file '"<< bufferObj->getStreamInfo().fsFile.string() <<"' "<<
92  "(slice: '" << item.first << "')");
93  }
94 
95  fileFormat.loadAllDataIntoMemory();
96  fileFormat.transferEnd();
97 
98  dataset = fileFormat.getDataset();
99 
100  if(!dataset->tagExists(DCM_ImagePositionPatient) || !dataset->tagExists(DCM_ImageOrientationPatient))
101  {
102  const std::string msg =
103  "Unable to split the series using ImagePositionPatient and ImageOrientationPatient. "
104  "Tag(s) missing.";
105  throw ::fwDicomIOFilter::exceptions::FilterFailure(msg);
106  }
107 
108  fwVec3d imagePosition;
109  for(unsigned int i = 0; i < 3; ++i)
110  {
111  dataset->findAndGetFloat64(DCM_ImagePositionPatient, imagePosition[i], i);
112  }
113 
114  fwVec3d imageOrientationU;
115  fwVec3d imageOrientationV;
116  for(unsigned int i = 0; i < 3; ++i)
117  {
118  dataset->findAndGetFloat64(DCM_ImageOrientationPatient, imageOrientationU[i], i);
119  dataset->findAndGetFloat64(DCM_ImageOrientationPatient, imageOrientationV[i], i+3);
120  }
121 
122  //Compute Z direction (cross product)
123  const fwVec3d zVector = ::fwMath::cross(imageOrientationU, imageOrientationV);
124 
125  //Compute dot product to get the index
126  const double index = ::fwMath::dot(imagePosition, zVector);
127 
128  //Compute spacing
129  const double spacing = index - previousIndex;
130  if(currentSeries && fabs(spacingBetweenSlices) < epsilon)
131  {
132  spacingBetweenSlices = spacing;
133  }
134 
135  // First frame or volume detected: We create a new Series
136  if(!currentSeries || (fabs(spacing - spacingBetweenSlices) > epsilon) )
137  {
138  if(currentSeries)
139  {
140  result.push_back(currentSeries);
141  currentSeries->setNumberOfInstances(currentSeries->getDicomContainer().size());
142  }
143  instanceNumber = 0;
144  currentSeries = ::fwMedData::DicomSeries::New();
145  currentSeries->deepCopy(series);
146  currentSeries->clearDicomContainer();
147  }
148 
149  currentSeries->addBinary(instanceNumber++, bufferObj);
150  previousIndex = index;
151  }
152 
153  // Push last series created
154  result.push_back(currentSeries);
155  currentSeries->setNumberOfInstances(currentSeries->getDicomContainer().size());
156 
157  if(result.size() > 1)
158  {
159  logger->warning("Series has been split according to slice positions.");
160  }
161 
162  return result;
163 }
164 
165 } // namespace splitter
166 } // namespace fwDicomIOFilter
virtual FWDICOMIOFILTER_API std::string getName() const override
Return the name of the filter.
virtual FWDICOMIOFILTER_API ~ImagePositionPatientSplitter()
Destructor.
fwDicomIOFilter contains filters used to pre-process images before reading.
base class for BufferObject Lock
virtual FWDICOMIOFILTER_API std::string getDescription() const override
Return the description of the filter.
FWDICOMIOFILTER_API ImagePositionPatientSplitter(::fwDicomIOFilter::IFilter::Key key)
Constructor.
virtual FWDICOMIOFILTER_API DicomSeriesContainerType apply(const ::fwMedData::DicomSeries::sptr &series, const ::fwLog::Logger::sptr &logger) const override
Override.
LockBase< T >::BufferType getBuffer() const
Returns BufferObject&#39;s buffer pointer.
Filter that uses the ImagePositionPatient tag to split the instances. For this filter to work properl...
Key class used to restrict access to Filter construction. See http://www.drdobbs.com/184402053.
static const std::string s_FILTER_DESCRIPTION
Filter description.
Base class for Dicom instance splitter.
Definition: ISplitter.hpp:23