fw4spl
SliceThicknessModifier.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/modifier/SliceThicknessModifier.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::modifier::SliceThicknessModifier );
22 
23 namespace fwDicomIOFilter
24 {
25 namespace modifier
26 {
27 
28 const std::string SliceThicknessModifier::s_FILTER_NAME = "Slice thickness modifier";
30  "Compute and modify slice thickness using <i>ImagePositionPatient</i> "
31  "and <i>ImageOrientationPatient</i> tags of the two first instances.";
32 
33 //-----------------------------------------------------------------------------
34 
36  IModifier()
37 {
38 }
39 
40 //-----------------------------------------------------------------------------
41 
43 {
44 }
45 
46 //-----------------------------------------------------------------------------
47 
49 {
51 }
52 
53 //-----------------------------------------------------------------------------
54 
56 {
58 }
59 
60 //-----------------------------------------------------------------------------
61 
62 SliceThicknessModifier::DicomSeriesContainerType SliceThicknessModifier::apply(
63  const ::fwMedData::DicomSeries::sptr& series,
64  const ::fwLog::Logger::sptr& logger) const
65 {
66  DicomSeriesContainerType result;
67 
68  if(series->getDicomContainer().size() < 2)
69  {
70  SLM_WARN("SliceThicknessModifier is being applied on a series containing only one slice.");
71  result.push_back(series);
72  return result;
73  }
74 
75  // Retrieve the two first instances
76  auto firstItem = series->getDicomContainer().begin();
77 
78  const ::fwMemory::BufferObject::sptr& firstBufferObj = firstItem->second;
79  const ::fwMemory::BufferObject::sptr& secondBufferObj = (++firstItem)->second;
80 
81  // Compute the slice thickness between the 2 first slices.
82  const double firstIndex = this->getInstanceZPosition(firstBufferObj);
83  const double secondIndex = this->getInstanceZPosition(secondBufferObj);
84  const double sliceThickness = std::abs(secondIndex - firstIndex);
85 
86  // Check that the computed sliceThickness doesn't match the sliceThickness of the first instance
87  const double currentSliceThickness = this->getSliceThickness(firstBufferObj);
88  const double epsilon = 1e-2;
89 
90  // If the computed sliceThickness doesn't match the sliceThickness value
91  // we add the computed value to the DicomSeries.
92  if(std::abs(sliceThickness - currentSliceThickness) > epsilon)
93  {
94  series->addComputedTagValue("SliceThickness", std::to_string(sliceThickness));
95  }
96 
97  result.push_back(series);
98  return result;
99 }
100 
101 //-----------------------------------------------------------------------------
102 
103 double SliceThicknessModifier::getInstanceZPosition(const ::fwMemory::BufferObject::sptr& bufferObj) const
104 {
105  DcmFileFormat fileFormat;
106  DcmDataset* dataset;
107 
108  const size_t buffSize = bufferObj->getSize();
109  ::fwMemory::BufferObject::Lock lock(bufferObj);
110  char* buffer = static_cast< char* >( lock.getBuffer() );
111 
112  DcmInputBufferStream is;
113  is.setBuffer(buffer, offile_off_t(buffSize));
114  is.setEos();
115 
116  fileFormat.transferInit();
117  if (!fileFormat.read(is).good())
118  {
119  FW_RAISE("Unable to read Dicom file '"<< bufferObj->getStreamInfo().fsFile.string() <<"'");
120  }
121 
122  fileFormat.loadAllDataIntoMemory();
123  fileFormat.transferEnd();
124 
125  dataset = fileFormat.getDataset();
126 
127  if(!dataset->tagExists(DCM_ImagePositionPatient) || !dataset->tagExists(DCM_ImageOrientationPatient))
128  {
129  const std::string msg = "Unable to compute the SliceThickness of the series.";
130  throw ::fwDicomIOFilter::exceptions::FilterFailure(msg);
131  }
132 
133  fwVec3d imagePosition;
134  for(unsigned int i = 0; i < 3; ++i)
135  {
136  dataset->findAndGetFloat64(DCM_ImagePositionPatient, imagePosition[i], i);
137  }
138 
139  fwVec3d imageOrientationU;
140  fwVec3d imageOrientationV;
141  for(unsigned int i = 0; i < 3; ++i)
142  {
143  dataset->findAndGetFloat64(DCM_ImageOrientationPatient, imageOrientationU[i], i);
144  dataset->findAndGetFloat64(DCM_ImageOrientationPatient, imageOrientationV[i], i+3);
145  }
146 
147  //Compute Z direction (cross product)
148  const fwVec3d zVector = ::fwMath::cross(imageOrientationU, imageOrientationV);
149 
150  //Compute dot product to get the index
151  const double index = ::fwMath::dot(imagePosition, zVector);
152 
153  return index;
154 }
155 
156 //-----------------------------------------------------------------------------
157 
158 double SliceThicknessModifier::getSliceThickness(const ::fwMemory::BufferObject::sptr& bufferObj) const
159 {
160  DcmFileFormat fileFormat;
161  OFCondition status;
162  DcmDataset* dataset;
163 
164  const size_t buffSize = bufferObj->getSize();
165  ::fwMemory::BufferObject::Lock lock(bufferObj);
166  char* buffer = static_cast< char* >( lock.getBuffer() );
167 
168  DcmInputBufferStream is;
169  is.setBuffer(buffer, offile_off_t(buffSize));
170  is.setEos();
171 
172  fileFormat.transferInit();
173  if (!fileFormat.read(is).good())
174  {
175  FW_RAISE("Unable to read Dicom file '"<< bufferObj->getStreamInfo().fsFile.string() <<"'");
176  }
177 
178  fileFormat.loadAllDataIntoMemory();
179  fileFormat.transferEnd();
180 
181  dataset = fileFormat.getDataset();
182 
183  double sliceThickness = 0.;
184  dataset->findAndGetFloat64(DCM_SliceThickness, sliceThickness);
185 
186  return sliceThickness;
187 }
188 
189 } // namespace modifier
190 } // namespace fwDicomIOFilter
static const std::string s_FILTER_NAME
Filter name.
virtual FWDICOMIOFILTER_API double getSliceThickness(const ::fwMemory::BufferObject::sptr &bufferObj) const
Get the SliceThickness value from an instance.
Base class for Dicom instance modifier.
Definition: IModifier.hpp:23
static const std::string s_FILTER_DESCRIPTION
Filter description.
fwDicomIOFilter contains filters used to pre-process images before reading.
base class for BufferObject Lock
#define SLM_WARN(message)
Definition: spyLog.hpp:261
LockBase< T >::BufferType getBuffer() const
Returns BufferObject&#39;s buffer pointer.
Key class used to restrict access to Filter construction. See http://www.drdobbs.com/184402053.
virtual FWDICOMIOFILTER_API std::string getDescription() const override
Return the description of the filter.
virtual FWDICOMIOFILTER_API double getInstanceZPosition(const ::fwMemory::BufferObject::sptr &bufferObj) const
Compute the Z coordinate of the slice according to the ImagePositionPatient and ImageOrientationPatie...
virtual FWDICOMIOFILTER_API ~SliceThicknessModifier()
Destructor.
FWDICOMIOFILTER_API SliceThicknessModifier(::fwDicomIOFilter::IFilter::Key key)
Constructor.
virtual FWDICOMIOFILTER_API DicomSeriesContainerType apply(const ::fwMedData::DicomSeries::sptr &series, const ::fwLog::Logger::sptr &logger) const override
Override.
Filter that uses the ImagepositionPatient tag to sort the instances. The position increases along the...
virtual FWDICOMIOFILTER_API std::string getName() const override
Return the name of the filter.