fw4spl
ImagePositionPatientSorter.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/sorter/ImagePositionPatientSorter.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::sorter::ImagePositionPatientSorter );
22 
23 namespace fwDicomIOFilter
24 {
25 namespace sorter
26 {
27 
28 const std::string ImagePositionPatientSorter::s_FILTER_NAME = "Image position patient sorter";
30  "Sort instances by computing image position using <i>ImagePositionPatient</i> "
31  "and <i>ImageOrientationPatient</i> tags.";
32 
33 //-----------------------------------------------------------------------------
34 
36  ISorter()
37 {
38 }
39 
40 //-----------------------------------------------------------------------------
41 
43 {
44 }
45 
46 //-----------------------------------------------------------------------------
47 
49 {
51 }
52 
53 //-----------------------------------------------------------------------------
54 
56 {
58 }
59 
60 //-----------------------------------------------------------------------------
61 
62 ImagePositionPatientSorter::DicomSeriesContainerType ImagePositionPatientSorter::apply(
63  const ::fwMedData::DicomSeries::sptr& series,
64  const ::fwLog::Logger::sptr& logger) const
65 {
66  DicomSeriesContainerType result;
67 
68  typedef std::map< double, ::fwMemory::BufferObject::sptr > SortedDicomMapType;
69  SortedDicomMapType sortedDicom;
70 
71  OFCondition status;
72  DcmDataset* dataset;
73 
74  for(const auto& item : series->getDicomContainer())
75  {
76  const ::fwMemory::BufferObject::sptr bufferObj = item.second;
77  const size_t buffSize = bufferObj->getSize();
78  ::fwMemory::BufferObject::Lock lock(bufferObj);
79  char* buffer = static_cast< char* >( lock.getBuffer() );
80 
81  DcmInputBufferStream is;
82  is.setBuffer(buffer, offile_off_t(buffSize));
83  is.setEos();
84 
85  DcmFileFormat fileFormat;
86  fileFormat.transferInit();
87  if (!fileFormat.read(is).good())
88  {
89  FW_RAISE("Unable to read Dicom file '"<< bufferObj->getStreamInfo().fsFile.string() <<"' "<<
90  "(slice: '" << item.first << "')");
91  }
92 
93  fileFormat.loadAllDataIntoMemory();
94  fileFormat.transferEnd();
95 
96  dataset = fileFormat.getDataset();
97 
98  if(!dataset->tagExists(DCM_ImagePositionPatient) || !dataset->tagExists(DCM_ImageOrientationPatient))
99  {
100  const std::string msg =
101  "Unable to split the series using ImagePositionPatient and ImageOrientationPatient. "
102  "Tag(s) missing.";
103  throw ::fwDicomIOFilter::exceptions::FilterFailure(msg);
104  }
105 
106  fwVec3d imagePosition;
107  for(unsigned int i = 0; i < 3; ++i)
108  {
109  dataset->findAndGetFloat64(DCM_ImagePositionPatient, imagePosition[i], i);
110  }
111 
112  fwVec3d imageOrientationU;
113  fwVec3d imageOrientationV;
114  for(unsigned int i = 0; i < 3; ++i)
115  {
116  dataset->findAndGetFloat64(DCM_ImageOrientationPatient, imageOrientationU[i], i);
117  dataset->findAndGetFloat64(DCM_ImageOrientationPatient, imageOrientationV[i], i+3);
118  }
119 
120  //Compute Z direction (cross product)
121  const fwVec3d zVector = ::fwMath::cross(imageOrientationU, imageOrientationV);
122 
123  //Compute dot product to get the index
124  const double index = ::fwMath::dot(imagePosition, zVector);
125 
126  sortedDicom[index] = bufferObj;
127  }
128 
129  if(sortedDicom.size() != series->getDicomContainer().size())
130  {
131  const std::string msg =
132  "Unable to sort the series using the ImagePositionPatient tag. Some images have the same "
133  "position meaning this series contains multiple volumes. Try to split the volumes using a different "
134  "filter.";
135  throw ::fwDicomIOFilter::exceptions::FilterFailure(msg);
136  }
137 
138  series->clearDicomContainer();
139  size_t index = 0;
140  for(const auto& item : sortedDicom)
141  {
142  series->addBinary(index++, item.second);
143  }
144 
145  result.push_back(series);
146 
147  logger->information("The instances have been sorted using the slices positions.");
148 
149  return result;
150 }
151 
152 } // namespace sorter
153 } // namespace fwDicomIOFilter
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.
fwDicomIOFilter contains filters used to pre-process images before reading.
base class for BufferObject Lock
virtual FWDICOMIOFILTER_API ~ImagePositionPatientSorter()
Destructor.
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.
FWDICOMIOFILTER_API ImagePositionPatientSorter(::fwDicomIOFilter::IFilter::Key key)
Constructor.
virtual FWDICOMIOFILTER_API DicomSeriesContainerType apply(const ::fwMedData::DicomSeries::sptr &series, const ::fwLog::Logger::sptr &logger) const override
Override.
Base class for Dicom instance sorter.
Definition: ISorter.hpp:23
static const std::string s_FILTER_DESCRIPTION
Filter description.
virtual FWDICOMIOFILTER_API std::string getDescription() const override
Return the description of the filter.