fw4spl
ImageStorageReader.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 "fwDcmtkIO/reader/ImageStorageReader.hpp"
8 
9 #include "fwDcmtkIO/reader/main/ImageLazyStream.hpp"
10 #include "fwDcmtkIO/reader/main/ImageReader.hpp"
11 #include "fwDcmtkIO/reader/rgblookup/ImageRGBLookupLazyStream.hpp"
12 #include "fwDcmtkIO/reader/rgblookup/ImageRGBLookupReader.hpp"
13 
14 #include <fwDataTools/helper/Array.hpp>
15 
16 #include <fwDicomTools/Image.hpp>
17 #include <fwDicomTools/Series.hpp>
18 
19 #include <fwMedData/ImageSeries.hpp>
20 
21 #include <boost/assign/list_of.hpp>
22 #include <boost/assign/std/vector.hpp>
23 
24 #include <dcmtk/config/osconfig.h>
25 #include <dcmtk/dcmdata/dcdeftag.h>
26 #include <dcmtk/dcmdata/dcfilefo.h>
27 #include <dcmtk/dcmdata/dcistrmb.h>
28 #include <dcmtk/dcmimgle/dcmimage.h>
29 #include <dcmtk/dcmnet/diutil.h>
30 
31 #include <algorithm>
32 
33 namespace fwDcmtkIO
34 {
35 namespace reader
36 {
37 
39 {
40 }
41 
42 //-----------------------------------------------------------------------------
43 
45 {
46 }
47 
48 //-----------------------------------------------------------------------------
49 
50 ::fwMedData::Series::sptr ImageStorageReader::read(const ::fwMedData::DicomSeries::csptr& series)
51 {
52  ::fwMedData::DicomSeries::SOPClassUIDContainerType sopClassUIDContainer = series->getSOPClassUIDs();
53  std::string sopClassUID = dcmFindNameOfUID(sopClassUIDContainer.begin()->c_str());
54 
55  ::fwMedData::ImageSeries::sptr imageSeries = ::fwDicomTools::Series::convertToImageSeries(series);
56  DicomContainerType instances = series->getDicomContainer();
57 
58  ::fwData::Image::sptr image = ::fwData::Image::New();
59  DcmFileFormat fileFormat;
60  OFCondition status;
61  DcmDataset* dataset;
62 
63  //Get informations from the first instance
64  const auto firstItem = series->getDicomContainer().begin();
65  const ::fwMemory::BufferObject::sptr bufferObj = firstItem->second;
66  const size_t buffSize = bufferObj->getSize();
67  const std::string dicomPath = bufferObj->getStreamInfo().fsFile.string();
68  ::fwMemory::BufferObject::Lock lock(bufferObj);
69  char* buffer = static_cast< char* >( lock.getBuffer() );
70 
71  DcmInputBufferStream is;
72  is.setBuffer(buffer, offile_off_t(buffSize));
73  is.setEos();
74 
75  fileFormat.transferInit();
76  if (!fileFormat.read(is).good())
77  {
78  FW_RAISE("Unable to read Dicom file '"<< dicomPath <<"' "<<
79  "(slice: '" << firstItem->first << "')");
80  }
81 
82  fileFormat.loadAllDataIntoMemory();
83  fileFormat.transferEnd();
84 
85  dataset = fileFormat.getDataset();
86 
87  DicomImage dicomImage(dataset, dataset->getOriginalXfer());
88 
89  FW_RAISE_IF("Unable to read the file: \""+dicomPath+"\"", status.bad() || (
90  dicomImage.getStatus() != EIS_Normal
91  && dicomImage.getStatus() != EIS_MissingAttribute
92  && dicomImage.getStatus() != EIS_NotSupportedValue
93  ));
94 
95  // Decompress data set if compressed
96  dataset->chooseRepresentation(EXS_LittleEndianExplicit, nullptr);
97 
98  if(dicomImage.getStatus() != EIS_MissingAttribute)
99  {
100  SLM_WARN("Some informations are missing. The file may have not been read properly.");
101  }
102 
103  //Spacing
104  double spacing[3];
105  dataset->findAndGetFloat64(DCM_PixelSpacing, spacing[0], 0);
106  dataset->findAndGetFloat64(DCM_PixelSpacing, spacing[1], 1);
107  dataset->findAndGetFloat64(DCM_SliceThickness, spacing[2]);
108 
109  if(series->hasComputedValues("SliceThickness"))
110  {
111  spacing[2] = ::boost::lexical_cast< double >(series->getComputedTagValues().at("SliceThickness"));
112  }
113 
114  if(spacing[0] == 0 || spacing[1] == 0 || spacing[2] == 0)
115  {
116  spacing[0] = spacing[1] = spacing[2] = 1;
117  OSLM_WARN("Invalid value for pixel spacing. Assuming pixel value is 1.");
118  }
119 
120  image->setSpacing(std::vector< double >(spacing, spacing+3));
121 
122  //Origin
123  //TODO: Compute the correct origin
124  double imagePosition[3];
125  dataset->findAndGetFloat64(DCM_ImagePositionPatient, imagePosition[0], 0);
126  dataset->findAndGetFloat64(DCM_ImagePositionPatient, imagePosition[1], 1);
127  dataset->findAndGetFloat64(DCM_ImagePositionPatient, imagePosition[2], 2);
128  image->setOrigin(::boost::assign::list_of(imagePosition[0])(imagePosition[1])(imagePosition[2]));
129 
130  //Size
131  unsigned short rows, columns;
132  dataset->findAndGetUint16(DCM_Rows, rows);
133  dataset->findAndGetUint16(DCM_Columns, columns);
134 
135  uint32_t depth;
136  if(instances.size() == 1)
137  {
138  OFString sframesNumber = "";
139 
140  if(dataset->findAndGetOFString(DCM_NumberOfFrames, sframesNumber).good())
141  {
142  depth = static_cast<uint32_t>(std::stoi(sframesNumber.c_str()));
143  }
144  else
145  {
146  depth = 1;
147  }
148  }
149  else
150  {
151  depth = static_cast<unsigned short>(instances.size());
152  }
153 
154  //FIXME: Remove depth for 2D images ?
155  image->setSize(::boost::assign::list_of(columns)(rows)(depth));
156 
157  //Window Center
158  double windowCenter = 0;
159  dataset->findAndGetFloat64(DCM_WindowCenter, windowCenter);
160  image->setWindowCenter(windowCenter);
161 
162  //Window Width
163  double windowWidth = 0;
164  dataset->findAndGetFloat64(DCM_WindowWidth, windowWidth);
165  image->setWindowWidth(windowWidth);
166 
167  //Number of components
168  OFString data;
169  dataset->findAndGetOFStringArray(DCM_PhotometricInterpretation, data);
170  std::string photometricInterpretation = data.c_str();
171  dataset->findAndGetOFStringArray(DCM_PixelPresentation, data);
172  std::string pixelPresentation = data.c_str();
173 
174  if(photometricInterpretation == "MONOCHROME2")
175  {
176  image->setNumberOfComponents(1);
177  }
178  else if(photometricInterpretation == "RGB" || photometricInterpretation == "YBR")
179  {
180  image->setNumberOfComponents(3);
181  }
182  else if(photometricInterpretation == "ARGB" || photometricInterpretation == "CMYK")
183  {
184  image->setNumberOfComponents(4);
185  }
186  else if(photometricInterpretation == "PALETTE COLOR" || pixelPresentation == "COLOR")
187  {
188  image->setNumberOfComponents(3);
189  }
190  else
191  {
192  FW_RAISE( "The photometric interpretation \"" << photometricInterpretation << "\" is not supported.");
193  }
194 
195  //Rescale Slope
196  double rescaleSlope;
197  double rescaleIntercept;
198  status = dataset->findAndGetFloat64(DCM_RescaleSlope, rescaleSlope);
199  rescaleSlope = (status.bad()) ? 1 : rescaleSlope;
200  status = dataset->findAndGetFloat64(DCM_RescaleIntercept, rescaleIntercept);
201  rescaleIntercept = (status.bad()) ? 0 : rescaleIntercept;
202 
203  //Type
204  unsigned short samplesPerPixel = 1;
205  unsigned short bitsAllocated = 8;
206  unsigned short bitsStored = 8;
207  unsigned short highBit = 7;
208  unsigned short pixelRepresentation = 0;
209 
210  dataset->findAndGetUint16(DCM_SamplesPerPixel, samplesPerPixel);
211  dataset->findAndGetUint16(DCM_BitsAllocated, bitsAllocated);
212  dataset->findAndGetUint16(DCM_BitsStored, bitsStored);
213  dataset->findAndGetUint16(DCM_HighBit, highBit);
214  dataset->findAndGetUint16(DCM_PixelRepresentation, pixelRepresentation);
215 
216  //Using lookup tables
217  if(photometricInterpretation == "COLOR" || photometricInterpretation == "PALETTE COLOR")
218  {
219  unsigned short colorBitsAllocated = 0;
220  dataset->findAndGetUint16(DCM_RedPaletteColorLookupTableDescriptor, colorBitsAllocated, 2);
221  bitsStored = bitsAllocated = colorBitsAllocated;
222  highBit = static_cast<unsigned short>(colorBitsAllocated-1);
223  }
224 
225  //Find image type
226  ::fwDicomTools::Image imageHelper(
227  samplesPerPixel, bitsAllocated, bitsStored, highBit, pixelRepresentation, rescaleSlope, rescaleIntercept);
228  ::fwTools::Type imageType = imageHelper.findImageTypeFromMinMaxValues();
229 
230  //Set image type
231  image->setType(imageType);
232 
233  //Direct reading mode
234  if(::fwMemory::BufferManager::getDefault()->getLoadingMode() == ::fwMemory::BufferManager::DIRECT)
235  {
236  SLM_INFO("Reading using DIRECT mode.");
237 
238  //Default read
239  if(photometricInterpretation != "PALETTE COLOR" && pixelPresentation != "COLOR")
240  {
241  this->directRead(image, instances, rows, columns, depth, rescaleSlope, rescaleIntercept,
242  pixelRepresentation, imageType);
243  }
244  //RGB lookup read
245  else
246  {
247  this->directRGBLookupRead(image, *dataset, instances, rows, columns, depth, bitsAllocated);
248  }
249 
250  }
251  //Lazy reading mode
252  else
253  {
254  SLM_INFO("Reading using LAZY mode.");
255 
256  //Default read
257  if(photometricInterpretation != "PALETTE COLOR" && pixelPresentation != "COLOR")
258  {
259  this->lazyRead(image, series, rows, columns, depth, rescaleSlope, rescaleIntercept, pixelRepresentation,
260  imageType);
261  }
262  //RGB lookup read
263  else
264  {
265  this->lazyRGBLookupRead(image, series, *dataset, instances, rows, columns, depth, bitsAllocated, imageType);
266  }
267  }
268 
269  // Add the image to the series
270  imageSeries->setImage(image);
271 
272  return imageSeries;
273 }
274 
275 //-----------------------------------------------------------------------------
276 
277 void ImageStorageReader::directRead(const ::fwData::Image::sptr& image,
278  DicomContainerType instances,
279  unsigned short rows, unsigned short columns,
280  int depth, double rescaleSlope,
281  double rescaleIntercept,
282  unsigned short pixelRepresentation,
283  ::fwTools::Type imageType)
284 {
285  //Allocate image
286  image->allocate();
287  ::fwData::Array::sptr array = image->getDataArray();
288  ::fwDataTools::helper::Array arrayHelper(array);
289 
290  //Fill image
291  ::fwDcmtkIO::reader::main::ImageReader::fillImageBuffer(rows, columns, depth, instances,
292  arrayHelper.getBuffer(), rescaleSlope, rescaleIntercept, pixelRepresentation,
293  imageType);
294 }
295 
296 //-----------------------------------------------------------------------------
297 
298 void ImageStorageReader::directRGBLookupRead(const ::fwData::Image::sptr& image,
299  DcmDataset& dataset,
300  DicomContainerType instances,
301  unsigned short rows, unsigned short columns,
302  int depth, unsigned short bitsAllocated)
303 {
304  //Allocate image
305  image->allocate();
306  ::fwData::Array::sptr array = image->getDataArray();
307  ::fwDataTools::helper::Array arrayHelper(array);
308 
309  unsigned short pixelValueBitsAllocated = 8;
310  dataset.findAndGetUint16(DCM_BitsAllocated, pixelValueBitsAllocated);
311 
312  // 16 bits allocated
313  if(bitsAllocated == 16)
314  {
315  const Uint16* redLookup;
316  const Uint16* greenLookup;
317  const Uint16* blueLookup;
318  // Those values are freed by the dataset destructor
319  dataset.findAndGetUint16Array(DCM_RedPaletteColorLookupTableData, redLookup);
320  dataset.findAndGetUint16Array(DCM_GreenPaletteColorLookupTableData, greenLookup);
321  dataset.findAndGetUint16Array(DCM_BluePaletteColorLookupTableData, blueLookup);
322 
323  if(pixelValueBitsAllocated == 16)
324  {
325  ::fwDcmtkIO::reader::rgblookup::ImageRGBLookupReader::fillImageBuffer<Uint16, Uint16>(rows,
326  columns, depth,
327  instances,
328  arrayHelper.getBuffer(), redLookup, greenLookup,
329  blueLookup);
330  }
331  else
332  {
333  ::fwDcmtkIO::reader::rgblookup::ImageRGBLookupReader::fillImageBuffer<Uint16, Uint8>(rows,
334  columns, depth,
335  instances,
336  arrayHelper.getBuffer(), redLookup, greenLookup,
337  blueLookup);
338  }
339  }
340  // 8 bits allocated
341  else
342  {
343  const Uint8* redLookup;
344  const Uint8* greenLookup;
345  const Uint8* blueLookup;
346  // Those values are freed by the dataset destructor
347  dataset.findAndGetUint8Array(DCM_RedPaletteColorLookupTableData, redLookup);
348  dataset.findAndGetUint8Array(DCM_GreenPaletteColorLookupTableData, greenLookup);
349  dataset.findAndGetUint8Array(DCM_BluePaletteColorLookupTableData, blueLookup);
350 
351  if(pixelValueBitsAllocated == 16)
352  {
353  ::fwDcmtkIO::reader::rgblookup::ImageRGBLookupReader::fillImageBuffer<Uint8, Uint16>(rows,
354  columns, depth,
355  instances,
356  arrayHelper.getBuffer(), redLookup, greenLookup,
357  blueLookup);
358  }
359  else
360  {
361  ::fwDcmtkIO::reader::rgblookup::ImageRGBLookupReader::fillImageBuffer<Uint8, Uint8>(rows,
362  columns, depth,
363  instances,
364  arrayHelper.getBuffer(), redLookup, greenLookup,
365  blueLookup);
366  }
367  }
368 }
369 
370 //-----------------------------------------------------------------------------
371 
372 void ImageStorageReader::lazyRead(const ::fwData::Image::sptr& image,
373  const ::fwMedData::DicomSeries::csptr& series,
374  unsigned short rows, unsigned short columns,
375  int depth, double rescaleSlope,
376  double rescaleIntercept,
377  unsigned short pixelRepresentation,
378  ::fwTools::Type imageType)
379 {
380  // Create information object
381  ::fwDcmtkIO::reader::main::ImageLazyInformation::sptr dcmInfo =
382  std::make_shared< ::fwDcmtkIO::reader::main::ImageLazyInformation >();
383  dcmInfo->m_dicomSeries = series;
384  dcmInfo->m_rows = rows;
385  dcmInfo->m_columns = columns;
386  dcmInfo->m_depth = depth;
387  dcmInfo->m_rescaleSlope = rescaleSlope;
388  dcmInfo->m_rescaleIntercept = rescaleIntercept;
389  dcmInfo->m_pixelRepresentation = pixelRepresentation;
390  dcmInfo->m_imageType = imageType;
391 
392  // Create streamer
393  ::fwMemory::BufferObject::sptr buffObj = image->getDataArray()->getBufferObject();
394  buffObj->setIStreamFactory(
395  std::make_shared< ::fwDcmtkIO::reader::main::ImageLazyStream >( dcmInfo ),
396  image->getSizeInBytes() );
397 }
398 
399 //-----------------------------------------------------------------------------
400 
401 void ImageStorageReader::lazyRGBLookupRead(const ::fwData::Image::sptr& image,
402  const ::fwMedData::DicomSeries::csptr& series,
403  DcmDataset& dataset,
404  DicomContainerType instances,
405  unsigned short rows, unsigned short columns,
406  int depth, unsigned short bitsAllocated,
407  ::fwTools::Type imageType)
408 {
409  unsigned short pixelValueBitsAllocated = 8;
410  dataset.findAndGetUint16(DCM_BitsAllocated, pixelValueBitsAllocated);
411 
412  // Create information object
413  ::fwDcmtkIO::reader::rgblookup::ImageRGBLookupLazyInformation::sptr dcmInfo =
414  std::make_shared< ::fwDcmtkIO::reader::rgblookup::ImageRGBLookupLazyInformation >();
415  dcmInfo->m_dicomSeries = series;
416  dcmInfo->m_rows = rows;
417  dcmInfo->m_columns = columns;
418  dcmInfo->m_depth = depth;
419  dcmInfo->m_bitsAllocated = bitsAllocated;
420  dcmInfo->m_pixelValueBitsAllocated = pixelValueBitsAllocated;
421  dcmInfo->m_imageType = imageType;
422 
423  // Create streamer
424  ::fwMemory::BufferObject::sptr buffObj = image->getDataArray()->getBufferObject();
425  buffObj->setIStreamFactory(
426  std::make_shared< ::fwDcmtkIO::reader::rgblookup::ImageRGBLookupLazyStream >( dcmInfo ),
427  image->getSizeInBytes() );
428 
429 }
430 
431 } //reader
432 } //fwDcmtkIO
static FWDICOMTOOLS_API std::shared_ptr< ::fwMedData::ImageSeries > convertToImageSeries(const std::shared_ptr< const ::fwMedData::DicomSeries > &series)
Convert a DicomSeries to an ImageSeries.
FWDICOMTOOLS_API::fwTools::Type findImageTypeFromMinMaxValues() const
Find Image Type.
static FWMEMORY_API BufferManager::sptr getDefault()
Returns the current BufferManager instance.
virtual FWDATATOOLS_API void * getBuffer()
Getter for the array buffer.
base class for BufferObject Lock
#define SLM_WARN(message)
Definition: spyLog.hpp:261
static void fillImageBuffer(unsigned int rows, unsigned int columns, unsigned int depth, DicomContainerType &instances, void *destination, double rescaleSlope, double rescaleIntercept, unsigned short pixelRepresentation,::fwTools::Type imageType)
Fill the buffer of an image.
LockBase< T >::BufferType getBuffer() const
Returns BufferObject&#39;s buffer pointer.
#define OSLM_WARN(message)
Definition: spyLog.hpp:263
Class describing an elementary C++ type aka unsigned char, signed char, .... int, float...
Definition: Type.hpp:32
virtual FWDCMTKIO_API::fwMedData::Series::sptr read(const ::fwMedData::DicomSeries::csptr &series)
Override.
FWDCMTKIO_API void lazyRGBLookupRead(const ::fwData::Image::sptr &image, const ::fwMedData::DicomSeries::csptr &series, DcmDataset &dataset, DicomContainerType instances, unsigned short rows, unsigned short columns, int depth, unsigned short bitsAllocated,::fwTools::Type imageType)
Read an image using lazy mode and perform a RGB lookup.
fwDcmtkIO contains classes used to pull Dicom images from a pacs using dcmtk library.
Definition: Codec.hpp:12
virtual FWDCMTKIO_API ~ImageStorageReader()
Destructor.
FWDCMTKIO_API void directRead(const ::fwData::Image::sptr &image, DicomContainerType instances, unsigned short rows, unsigned short columns, int depth, double rescaleSlope, double rescaleIntercept, unsigned short pixelRepresentation,::fwTools::Type imageType)
Read an image using direct mode.
#define SLM_INFO(message)
Definition: spyLog.hpp:250
FWDCMTKIO_API void directRGBLookupRead(const ::fwData::Image::sptr &image, DcmDataset &dataset, DicomContainerType instances, unsigned short rows, unsigned short columns, int depth, unsigned short bitsAllocated)
Read an image using direct mode and perform a RGB lookup.
Helper to manage array buffer. Lock the buffer before to modify it.
FWDCMTKIO_API ImageStorageReader()
Constructor.
FWDCMTKIO_API void lazyRead(const ::fwData::Image::sptr &image, const ::fwMedData::DicomSeries::csptr &series, unsigned short rows, unsigned short columns, int depth, double rescaleSlope, double rescaleIntercept, unsigned short pixelRepresentation,::fwTools::Type imageType)
Read an image using lazy mode.